diff --git a/src/endpoints/auth.ts b/src/endpoints/auth.ts index 43f8a37..7e2e70e 100644 --- a/src/endpoints/auth.ts +++ b/src/endpoints/auth.ts @@ -33,9 +33,9 @@ async function login(request: UnwrappedRequest): Promise httpOnly: true, secure: true, maxAge: tokenLifeSpanInDays * 24 * 60 * 60, - path: '/api/auth/token' + path: '/api/auth/token', }); - return new OkResponse(); + return new OkResponse({ token }); } catch (error: any) { return new ErrorResponse(error as Error); } diff --git a/src/endpoints/collections.ts b/src/endpoints/collections.ts index 1ce1ed7..3247fc0 100644 --- a/src/endpoints/collections.ts +++ b/src/endpoints/collections.ts @@ -1,17 +1,12 @@ import { orm } from '../orm/orm'; import { UnwrappedRequest } from '../utilities/guard'; import { CreatedResponse, ErrorResponse, OkResponse, PagedResponse } from '../utilities/responseHelper'; -import { - GameToCollectionRequest, - CreateCollectionRequest, - UpdateCollectionRequest, -} from '../utilities/requestModels'; +import { GameToCollectionRequest, CreateCollectionRequest, UpdateCollectionRequest } from '../utilities/requestModels'; import { CollectionId, GameId } from '../utilities/secureIds'; async function create(request: UnwrappedRequest): Promise { try { - const newPlayer = await orm.collections.create(request.body, request.claims); - return new CreatedResponse(newPlayer); + return new CreatedResponse(await orm.collections.create(request.body, request.claims)); } catch (error: any) { return new ErrorResponse(error as Error); } diff --git a/src/endpoints/games.ts b/src/endpoints/games.ts index 9ec9c82..2dd1945 100644 --- a/src/endpoints/games.ts +++ b/src/endpoints/games.ts @@ -1,23 +1,21 @@ import { orm } from '../orm/orm'; import { UnwrappedRequest } from '../utilities/guard'; import { CreatedResponse, ErrorResponse, OkResponse, PagedResponse } from '../utilities/responseHelper'; -import { - CreateGameRequest, - UpdateGameRequest, -} from '../utilities/requestModels'; +import { CreateGameRequest, UpdateGameRequest } from '../utilities/requestModels'; import { GameId } from '../utilities/secureIds'; async function create(request: UnwrappedRequest): Promise { try { - const newUser = await orm.games.create( - { - name: request.body.name, - bggId: request.body.bggId, - imagePath: request.body.imagePath, - }, - request.claims, + return new CreatedResponse( + await orm.games.create( + { + name: request.body.name, + bggId: request.body.bggId, + imagePath: request.body.imagePath, + }, + request.claims, + ), ); - return new CreatedResponse(newUser); } catch (error: any) { return new ErrorResponse(error as Error); } @@ -33,13 +31,7 @@ async function get(request: UnwrappedRequest): Promise { async function update(request: UnwrappedRequest): Promise { try { - return new OkResponse( - await orm.games.update( - GameId.fromHash(request.params.id), - request.body, - request.claims, - ), - ); + return new OkResponse(await orm.games.update(GameId.fromHash(request.params.id), request.body, request.claims)); } catch (error: any) { return new ErrorResponse(error as Error); } diff --git a/src/endpoints/invites.ts b/src/endpoints/invites.ts index feadfae..fa841ea 100644 --- a/src/endpoints/invites.ts +++ b/src/endpoints/invites.ts @@ -1,22 +1,18 @@ import { orm } from '../orm/orm'; import { UnwrappedRequest } from '../utilities/guard'; import { CreatedResponse, ErrorResponse } from '../utilities/responseHelper'; -import { - AcceptInviteRequest, - InviteUserRequest, -} from '../utilities/requestModels'; +import { AcceptInviteRequest, InviteUserRequest } from '../utilities/requestModels'; import { PlayerId, UserId } from '../utilities/secureIds'; async function create(request: UnwrappedRequest): Promise { try { - const newUser = await orm.invites.create( - { + return new CreatedResponse( + await orm.invites.create({ ...request.body, playerId: PlayerId.fromHash(request.body.playerId), invitedByUserId: request.claims.userId as UserId, - }, + }), ); - return new CreatedResponse(newUser); } catch (error: any) { return new ErrorResponse(error as Error); } @@ -24,8 +20,7 @@ async function create(request: UnwrappedRequest): Promise): Promise { try { - const newUser = await orm.invites.accept(request.body); - return new CreatedResponse(newUser); + return new CreatedResponse(await orm.invites.accept(request.body)); } catch (error: any) { return new ErrorResponse(error as Error); } diff --git a/src/endpoints/matches.ts b/src/endpoints/matches.ts index 4dd27ce..950b684 100644 --- a/src/endpoints/matches.ts +++ b/src/endpoints/matches.ts @@ -7,14 +7,15 @@ import { MatchParticipant } from '../orm/matches'; async function create(request: UnwrappedRequest): Promise { try { - const newUser = await orm.matches.create({ - gameId: GameId.fromHash(request.body.gameId), - ownerId: request.claims.userId as UserId, - participants: request.body.participants.map( - (x) => new MatchParticipant(PlayerId.fromHash(x.playerId), x.standing), - ), - }); - return new CreatedResponse(newUser); + return new CreatedResponse( + await orm.matches.create({ + gameId: GameId.fromHash(request.body.gameId), + ownerId: request.claims.userId as UserId, + participants: request.body.participants.map( + (x) => new MatchParticipant(PlayerId.fromHash(x.playerId), x.standing), + ), + }), + ); } catch (error: any) { return new ErrorResponse(error as Error); } diff --git a/src/endpoints/players.ts b/src/endpoints/players.ts index c47435f..1b05133 100644 --- a/src/endpoints/players.ts +++ b/src/endpoints/players.ts @@ -6,8 +6,7 @@ import { PlayerId } from '../utilities/secureIds'; async function create(request: UnwrappedRequest): Promise { try { - const newPlayer = await orm.players.create(request.body); - return new CreatedResponse(newPlayer); + return new CreatedResponse(await orm.players.create(request.body)); } catch (error: any) { return new ErrorResponse(error as Error); } diff --git a/src/endpoints/users.ts b/src/endpoints/users.ts index 55a2ae8..8608d8d 100644 --- a/src/endpoints/users.ts +++ b/src/endpoints/users.ts @@ -6,13 +6,12 @@ import { PlayerId, UserId } from '../utilities/secureIds'; async function create(request: UnwrappedRequest): Promise { try { - const newUser = await orm.users.create( - { + return new CreatedResponse( + await orm.users.create({ ...request.body, playerId: PlayerId.fromHash(request.body.playerId), - } + }), ); - return new CreatedResponse(newUser); } catch (error: any) { return new ErrorResponse(error as Error); } @@ -28,9 +27,7 @@ async function get(request: UnwrappedRequest): Promise { async function update(request: UnwrappedRequest): Promise { try { - return new OkResponse( - await orm.users.update(UserId.fromHash(request.params.id), request.body, request.claims), - ); + return new OkResponse(await orm.users.update(UserId.fromHash(request.params.id), request.body, request.claims)); } catch (error: any) { return new ErrorResponse(error as Error); } diff --git a/src/orm/circles.ts b/src/orm/circles.ts index ee78fbe..5351c37 100644 --- a/src/orm/circles.ts +++ b/src/orm/circles.ts @@ -51,20 +51,20 @@ export class CircleOrm { ${model.name}, ${model.isPublic}, ${model.colour})`; - const newCircleId: string = (first(await sql`SELECT lastval();`) as any)?.lastval as string; + const newRecordId: string = (first(await sql`SELECT lastval();`) as any)?.lastval as string; - return await this.get(CircleId.fromID(newCircleId)); + return await this.get(CircleId.fromID(newRecordId)); } async get(id: CircleId, claims?: Claims): Promise { - const circleResult: any = first( + const record: any = first( await sql`SELECT * FROM circles WHERE id = ${id.raw} LIMIT 1`, ); - if (!circleResult) { + if (!record) { throw new NotFoundError('No matching game exists'); } @@ -72,8 +72,8 @@ export class CircleOrm { if ( claims && !claims.test(Claims.ADMIN) && - !(claims.test(Claims.CIRCLES.PUBLIC.READ) && circleResult.is_public) && - !(claims.test(Claims.CIRCLES.OWNED.READ) && circleResult.owning_user_id === claims.userId.raw) && + !(claims.test(Claims.CIRCLES.PUBLIC.READ) && record.is_public) && + !(claims.test(Claims.CIRCLES.OWNED.READ) && record.owning_user_id === claims.userId.raw) && !( claims.test(Claims.CIRCLES.PRIVATE.READ_IF_MEMBER) && (user = await orm.users.get(claims.userId)) && @@ -86,25 +86,25 @@ export class CircleOrm { } return new Circle({ - id: CircleId.fromID(circleResult.id), - owningUserId: UserId.fromID(circleResult.owning_user_id), - name: circleResult.name, - isPublic: circleResult.is_public, - imagePath: circleResult.image_path, - colour: circleResult.colour, + id: CircleId.fromID(record.id), + owningUserId: UserId.fromID(record.owning_user_id), + name: record.name, + isPublic: record.is_public, + imagePath: record.image_path, + colour: record.colour, }); } async update(id: CircleId, patch: UpdateCircleRequest): Promise { - const recordToUpdate = await this.get(id); - recordToUpdate.name = patch.name ?? recordToUpdate.name; - recordToUpdate.colour = patch.colour ?? recordToUpdate.colour; - recordToUpdate.imagePath = patch.imagePath ?? recordToUpdate.imagePath; + const circle = await this.get(id); + circle.name = patch.name ?? circle.name; + circle.colour = patch.colour ?? circle.colour; + circle.imagePath = patch.imagePath ?? circle.imagePath; await sql`UPDATE circles - SET name=${recordToUpdate.name}, - colour=${recordToUpdate.colour}, - image_path=${recordToUpdate.imagePath} + SET name=${circle.name}, + colour=${circle.colour}, + image_path=${circle.imagePath} WHERE id = ${id.raw}`; return await this.get(id); @@ -131,8 +131,12 @@ export class CircleOrm { return; } - async invite(circleId: CircleId, relatedRecord: PlayerId | UserId | string | undefined, claims: Claims): Promise { - if(relatedRecord === undefined) { + async invite( + circleId: CircleId, + relatedRecord: PlayerId | UserId | string | undefined, + claims: Claims, + ): Promise { + if (relatedRecord === undefined) { throw new BadRequestError(); } @@ -164,18 +168,18 @@ export class CircleOrm { query: (query: string) => Promise = memo<(query: string) => Promise, Circle[]>(this.#query); async #query(query: string): Promise { - const dbResult: any = await sql` SELECT + const queryResult: any = await sql` SELECT id, name, owning_user_id, is_public FROM (SELECT *, SIMILARITY(${query}, name) as similarity FROM circles WHERE is_public=true) WHERE similarity > 0 ORDER BY similarity LIMIT 5;`; - if (!dbResult) { + if (!queryResult) { throw new NotFoundError('No matching circles exists'); } - return dbResult.map( + return queryResult.map( (x: { id: string; name: string; owning_user_id: string; is_public: boolean }) => new Circle({ id: CircleId.fromID(x.id), diff --git a/src/orm/claims.ts b/src/orm/claims.ts index 3e51412..eae60de 100644 --- a/src/orm/claims.ts +++ b/src/orm/claims.ts @@ -8,8 +8,8 @@ export class Claims extends ClaimDefinition { constructor(raw?: { userId?: string | UserId; claims?: string[] }) { super(); - if(raw?.userId instanceof UserId) { - this.userId = raw.userId + if (raw?.userId instanceof UserId) { + this.userId = raw.userId; } else { this.userId = UserId.fromHash(raw?.userId ?? ''); } @@ -30,20 +30,20 @@ export class Claims extends ClaimDefinition { export class ClaimsOrm { async getByUserId(userId: UserId): Promise { - const dbResults: any[] = await sql`SELECT c.name + const records: any[] = await sql`SELECT c.name from user_claims as uc JOIN claims as c on uc.claim_id = c.id where uc.user_id = ${userId.raw};`; return new Claims({ userId: userId, - claims: dbResults.map((x) => x.name), + claims: records.map((x) => x.name), }); } async getDefaultClaims(): Promise { - const dbResults: any[] = await sql`SELECT id + const records: any[] = await sql`SELECT id FROM claims WHERE is_default = true;`; - return dbResults.map((x) => x.id); + return records.map((x) => x.id); } } diff --git a/src/orm/collections.ts b/src/orm/collections.ts index 7036cf1..7e38503 100644 --- a/src/orm/collections.ts +++ b/src/orm/collections.ts @@ -24,42 +24,42 @@ export class CollectionsOrm { async create(model: { name: string }, claims: Claims): Promise { await sql`INSERT INTO collections (name, user_id) VALUES (${model.name}, ${claims?.userId?.raw})`; - const newPCollectionId: string = (first(await sql`SELECT lastval();`) as any)?.lastval as string; + const newRecordId: string = (first(await sql`SELECT lastval();`) as any)?.lastval as string; - return await this.get(CollectionId.fromID(newPCollectionId)); + return await this.get(CollectionId.fromID(newRecordId)); } async get(id: CollectionId, claims?: Claims): Promise { - const dbResult: any = await sql`SELECT + const records: any = await sql`SELECT c.id AS collection_id, c.name AS collection_name, c.user_id AS user_id, g.id AS game_id, g.name AS game_name FROM collections c - LEFT JOIN collection_games cg ON cg.collection_id = c.id - LEFT JOIN games g ON g.id = cg.game_id + LEFT JOIN collection_games cg ON cg.collection_id = c.id + LEFT JOIN games g ON g.id = cg.game_id WHERE c.id = ${id.raw}`; if ( claims && !( claims.test(Claims.ADMIN, Claims.COLLECTIONS.UNOWNED.READ) || (Claims.test(claims, Claims.COLLECTIONS.OWNED.READ) && - dbResult?.[0]?.user_id === claims?.userId?.raw) + records?.[0]?.user_id === claims?.userId?.raw) ) ) { throw new UnauthorizedError(); } - if (!dbResult?.length) { + if (!records?.length) { throw new NotFoundError('No matching player exists'); } return new Collection({ - id: CollectionId.fromID(dbResult[0].collection_id), - name: dbResult[0].collection_name, - ownerId: UserId.fromID(dbResult[0].user_id), - games: dbResult + id: CollectionId.fromID(records[0].collection_id), + name: records[0].collection_name, + ownerId: UserId.fromID(records[0].user_id), + games: records .filter((x: { game_id: string; game_name: string }) => x.game_id) .map( (x: { game_id: string; game_name: string }) => diff --git a/src/orm/games.ts b/src/orm/games.ts index 74afbfb..dd97ef3 100644 --- a/src/orm/games.ts +++ b/src/orm/games.ts @@ -24,44 +24,44 @@ export class GamesOrm { async create(model: CreateGameRequest, claims?: Claims): Promise { await sql`INSERT INTO games (name, image_path, bgg_id) VALUES (${model.name}, ${claims?.test(Claims.GAMES.MANAGE_IMAGES) ? model.imagePath : null}, ${model.bggId})`; - const newGameId: string = (first(await sql`SELECT lastval();`) as any)?.lastval as string; + const newRecordId: string = (first(await sql`SELECT lastval();`) as any)?.lastval as string; - return await this.get(GameId.fromID(newGameId)); + return await this.get(GameId.fromID(newRecordId)); } async get(id: GameId): Promise { - const dbResult: any = first( + const record: any = first( await sql`SELECT * FROM games WHERE id = ${id.raw} LIMIT 1`, ); - if (!dbResult) { + if (!record) { throw new NotFoundError('No matching game exists'); } return new Game({ - id: GameId.fromID(dbResult.id), - name: dbResult.name, - bggId: dbResult.bgg_id, - imagePath: dbResult.image_path, + id: GameId.fromID(record.id), + name: record.name, + bggId: record.bgg_id, + imagePath: record.image_path, }); } async update(id: GameId, patch: UpdateGameRequest, claims?: Claims): Promise { - const gameToUpdate = await this.get(id); - gameToUpdate.name = patch.name ?? gameToUpdate.name; - gameToUpdate.bggId = patch.bggId ?? gameToUpdate.bggId; + const game = await this.get(id); + game.name = patch.name ?? game.name; + game.bggId = patch.bggId ?? game.bggId; if (claims?.test(Claims.GAMES.MANAGE_IMAGES)) { - gameToUpdate.imagePath = patch.imagePath ?? gameToUpdate.imagePath; + game.imagePath = patch.imagePath ?? game.imagePath; } await sql`UPDATE games - SET name=${gameToUpdate.name}, - bgg_id=${gameToUpdate.bggId}, - image_path=${gameToUpdate.imagePath} + SET name=${game.name}, + bgg_id=${game.bggId}, + image_path=${game.imagePath} WHERE id = ${id.raw}`; return await this.get(id); @@ -90,18 +90,18 @@ export class GamesOrm { query: (query: string) => Promise = memo<(query: string) => Promise, Game[]>(this.#query); async #query(query: string): Promise { - const dbResult: any = await sql` SELECT + const records: any = await sql` SELECT id, name FROM (SELECT *, SIMILARITY(${query}, name) as similarity FROM games) WHERE similarity > 0 ORDER BY similarity LIMIT 5;`; - if (!dbResult) { + if (!records) { throw new NotFoundError('No matching game exists'); } - return dbResult.map( + return records.map( (x: { id: string; name: string }) => new Game({ id: GameId.fromID(x.id), diff --git a/src/orm/invites.ts b/src/orm/invites.ts index 488d43d..26a6fc6 100644 --- a/src/orm/invites.ts +++ b/src/orm/invites.ts @@ -35,7 +35,7 @@ export class InvitesOrm { throw new UnauthorizedError('Invite allowance reached.'); } - const inviteExists = ( + const doesInviteExist = ( first( await sql`SELECT COUNT(*) > 0 AS exists FROM user_invites @@ -45,12 +45,12 @@ export class InvitesOrm { exists: boolean; } )?.exists; - if (inviteExists) { + if (doesInviteExist) { throw new BadRequestError('Player has already been invited.'); } } - const playerHasUser = ( + const isPlayerAssociatedWithUser = ( first( await sql`SELECT COUNT(*) > 0 AS exists FROM users @@ -60,7 +60,7 @@ export class InvitesOrm { exists: boolean; } )?.exists; - if (playerHasUser) { + if (isPlayerAssociatedWithUser) { throw new BadRequestError('User has already been invited.'); } @@ -69,7 +69,7 @@ export class InvitesOrm { const invitationCode = createRandomString(6); await sql`INSERT INTO user_invites (invite_code, email, player_id, invited_by_user_id) VALUES (${invitationCode}, ${email}, ${playerId.raw}, ${invitedByUserId.raw})`; - const newInviteId: string = (first(await sql`SELECT lastval();`) as any)?.lastval as string; + const newRecordId: string = (first(await sql`SELECT lastval();`) as any)?.lastval as string; const resend = new Resend(process.env.RESEND_KEY); const resendResponse = await resend.emails.send({ @@ -83,51 +83,51 @@ export class InvitesOrm { throw new InternalServerError(); } - await sql`UPDATE user_invites SET was_email_sent = true WHERE id=${newInviteId}`; + await sql`UPDATE user_invites SET was_email_sent = true WHERE id=${newRecordId}`; return; } async accept({ inviteCode, password }: { inviteCode: string; password: string }): Promise { - const invite: { + const record: { id: string; email: string; player_id: string; accepted: boolean; } = first(await sql`SELECT * FROM user_invites WHERE invite_code=${inviteCode} LIMIT 1`); - if (!invite) { + if (!record) { throw new NotFoundError('Invalid invite code'); } - if (invite.accepted) { + if (record.accepted) { throw new UnauthorizedError('Invite already accepted'); } - const playerHasUser = ( + const isPlayerAssociatedWithUser = ( first( await sql`SELECT COUNT(*) > 0 AS exists FROM users - WHERE player_id = ${invite.player_id} - OR email = ${invite.email}`, + WHERE player_id = ${record.player_id} + OR email = ${record.email}`, ) as { exists: boolean; } )?.exists; - if (playerHasUser) { + if (isPlayerAssociatedWithUser) { throw new BadRequestError('User has already been invited.'); } - const createdUser = await orm.users.create({ - email: invite.email, - playerId: PlayerId.fromID(invite.player_id), + const user = await orm.users.create({ + email: record.email, + playerId: PlayerId.fromID(record.player_id), password, }); await sql`UPDATE user_invites SET accepted = true - WHERE id = ${invite.id}`; + WHERE id = ${record.id}`; - return createdUser; + return user; } } diff --git a/src/orm/matches.ts b/src/orm/matches.ts index f6e5961..17f51d0 100644 --- a/src/orm/matches.ts +++ b/src/orm/matches.ts @@ -176,10 +176,11 @@ export class MatchOrm { return; } - async removePlayer(matchId: MatchId, participantId: UserId): Promise; - async removePlayer(matchId: MatchId, participantId: PlayerId): Promise { - let playerId: PlayerId = participantId; - if (participantId instanceof UserId) { + async removePlayer(matchId: MatchId, participantId: UserId | PlayerId): Promise { + let playerId: PlayerId; + if (participantId instanceof PlayerId) { + playerId = participantId; + } else { playerId = (await orm.users.get(participantId))?.playerId; if (!playerId) { throw new BadRequestError('User is not a participant'); diff --git a/src/orm/players.ts b/src/orm/players.ts index b369bfb..611e0d4 100644 --- a/src/orm/players.ts +++ b/src/orm/players.ts @@ -32,9 +32,9 @@ export class PlayersOrm { async create(model: { name: string }): Promise { await sql`INSERT INTO players (name) VALUES (${model.name})`; - const newPlayerId: string = (first(await sql`SELECT lastval();`) as any)?.lastval as string; + const newRecordId: string = (first(await sql`SELECT lastval();`) as any)?.lastval as string; - return await this.get(PlayerId.fromID(newPlayerId)); + return await this.get(PlayerId.fromID(newRecordId)); } async get(id: PlayerId, claims?: Claims): Promise { @@ -47,23 +47,23 @@ export class PlayersOrm { } } - const dbResult: any = first( + const record: any = first( await sql`SELECT * FROM players WHERE id = ${id.raw} LIMIT 1`, ); - if (!dbResult) { + if (!record) { throw new NotFoundError('No matching player exists'); } return new Player({ - id: PlayerId.fromID(dbResult.id), - name: dbResult.name, - elo: parseInt(dbResult.elo), - isRatingLocked: dbResult.is_rating_locked, - canBeMultiple: dbResult.can_be_multiple, + id: PlayerId.fromID(record.id), + name: record.name, + elo: parseInt(record.elo), + isRatingLocked: record.is_rating_locked, + canBeMultiple: record.can_be_multiple, }); } @@ -119,15 +119,15 @@ export class PlayersOrm { } } - const playerToUpdate = await this.get(id); - playerToUpdate.name = patch.name ?? playerToUpdate.name; - playerToUpdate.isRatingLocked = patch.isRatingLocked ?? playerToUpdate.isRatingLocked; - playerToUpdate.canBeMultiple = patch.canBeMultiple ?? playerToUpdate.canBeMultiple; + const player = await this.get(id); + player.name = patch.name ?? player.name; + player.isRatingLocked = patch.isRatingLocked ?? player.isRatingLocked; + player.canBeMultiple = patch.canBeMultiple ?? player.canBeMultiple; await sql`UPDATE players - SET name=${playerToUpdate.name}, - is_rating_locked=${playerToUpdate.isRatingLocked}, - can_be_multiple=${playerToUpdate.canBeMultiple} + SET name=${player.name}, + is_rating_locked=${player.isRatingLocked}, + can_be_multiple=${player.canBeMultiple} WHERE id = ${id.raw}`; return await this.get(id); diff --git a/src/orm/user.ts b/src/orm/user.ts index 5471868..f1c626c 100644 --- a/src/orm/user.ts +++ b/src/orm/user.ts @@ -47,15 +47,15 @@ export class UsersOrm { const passwordHash = await argon2.hash(password); await sql`INSERT INTO users (email, pass_hash, player_id) VALUES (${email}, ${passwordHash}, ${playerId.raw})`; - const newUserId: UserId = UserId.fromID((first(await sql`SELECT lastval();`) as any)?.lastval as string); + const newRecordId: string = (first(await sql`SELECT lastval();`) as any)?.lastval as string; await sql.transaction(async (tx) => { for (let i in defaultClaims) { await tx`INSERT INTO user_claims (user_id, claim_id) - VALUES (${newUserId.raw}, ${defaultClaims[i]})`; + VALUES (${newRecordId}, ${defaultClaims[i]})`; } }); - return await this.get(newUserId); + return await this.get(UserId.fromID(newRecordId)); } async get(id: UserId, claims?: Claims): Promise { @@ -69,7 +69,7 @@ export class UsersOrm { throw new UnauthorizedError(); } - const dbResult: any = first( + const record: any = first( await sql`SELECT * FROM users WHERE id = ${id.raw} @@ -77,21 +77,20 @@ export class UsersOrm { LIMIT 1`, ); - if (!dbResult) { + if (!record) { throw new NotFoundError('No matching user exists'); } return new User( - UserId.fromID(dbResult.id), - PlayerId.fromID(dbResult.player_id), - dbResult.email, - dbResult.is_admin, + UserId.fromID(record.id), + PlayerId.fromID(record.player_id), + record.email, + record.is_admin, ); } async getByPlayer(id: PlayerId, claims?: Claims): Promise { - - const dbResult: any = first( + const record: any = first( await sql`SELECT * FROM users WHERE player_id = ${id.raw} @@ -99,7 +98,7 @@ export class UsersOrm { LIMIT 1`, ); - if (!dbResult) { + if (!record) { throw new NotFoundError('No matching user exists'); } @@ -107,22 +106,22 @@ export class UsersOrm { claims && !( claims.test(Claims.ADMIN, Claims.USERS.OTHER.READ) || - (claims.test(Claims.USERS.SELF.READ) && dbResult.id === claims?.userId.raw) + (claims.test(Claims.USERS.SELF.READ) && record.id === claims?.userId.raw) ) ) { throw new UnauthorizedError(); } return new User( - UserId.fromID(dbResult.id), - PlayerId.fromID(dbResult.player_id), - dbResult.email, - dbResult.is_admin, + UserId.fromID(record.id), + PlayerId.fromID(record.player_id), + record.email, + record.is_admin, ); } - async getByEmail(email:string, claims?: Claims): Promise { - const dbResult: any = first( + async getByEmail(email: string, claims?: Claims): Promise { + const record: any = first( await sql`SELECT * FROM users WHERE email = ${email} @@ -130,7 +129,7 @@ export class UsersOrm { LIMIT 1`, ); - if (!dbResult) { + if (!record) { throw new NotFoundError('No matching user exists'); } @@ -138,17 +137,17 @@ export class UsersOrm { claims && !( claims.test(Claims.ADMIN, Claims.USERS.OTHER.READ) || - (claims.test(Claims.USERS.SELF.READ) && dbResult.id === claims?.userId.raw) + (claims.test(Claims.USERS.SELF.READ) && record.id === claims?.userId.raw) ) ) { throw new UnauthorizedError(); } return new User( - UserId.fromID(dbResult.id), - PlayerId.fromID(dbResult.player_id), - dbResult.email, - dbResult.is_admin, + UserId.fromID(record.id), + PlayerId.fromID(record.player_id), + record.email, + record.is_admin, ); } @@ -163,15 +162,15 @@ export class UsersOrm { throw new UnauthorizedError(); } - const userToUpdate = await this.get(id); + const user = await this.get(id); if (!claims || claims.test(Claims.ADMIN)) { - userToUpdate.isActive = patch.isActive ?? userToUpdate.isActive; - userToUpdate.isAdmin = patch.isAdmin ?? userToUpdate.isAdmin; + user.isActive = patch.isActive ?? user.isActive; + user.isAdmin = patch.isAdmin ?? user.isAdmin; } await sql`UPDATE users - SET is_active=${userToUpdate.isActive}, - is_admin=${userToUpdate.isAdmin} + SET is_active=${user.isActive}, + is_admin=${user.isAdmin} WHERE id = ${id.raw}`; return await this.get(id); @@ -203,35 +202,35 @@ export class UsersOrm { } async verifyCredentials(email: string, password: string): Promise<{ userId: UserId; refreshCount: string } | null> { - const dbResult: any = first( + const record: any = first( await sql`SELECT * FROM users WHERE email = ${email} AND is_active = true limit 1`, ); - if (!dbResult) { + if (!record) { throw new UnauthorizedError(); } - if (!(await argon2.verify(dbResult.pass_hash, password))) { + if (!(await argon2.verify(record.pass_hash, password))) { return null; } return { - userId: UserId.fromID(dbResult.id), - refreshCount: dbResult.refresh_count, + userId: UserId.fromID(record.id), + refreshCount: record.refresh_count, }; } async verifyRefreshCount(id: UserId, refreshCount: string): Promise { - const dbResult: any = first( + const record: any = first( await sql`SELECT * FROM users WHERE id = ${id.raw} LIMIT 1`, ); - return dbResult.refresh_count === refreshCount; + return record.refresh_count === refreshCount; } async changePassword(id: UserId, oldPassword: string | null, newPassword: string, claims?: Claims): Promise { @@ -244,14 +243,14 @@ export class UsersOrm { throw new BadRequestError('Password is required'); } - const dbUser: any = first( + const record: any = first( await sql`SELECT * FROM users WHERE id = ${id.raw} LIMIT 1`, ); - if (!isAdmin && !(await argon2.verify(dbUser.pass_hash, oldPassword as string))) { + if (!isAdmin && !(await argon2.verify(record.pass_hash, oldPassword as string))) { throw new UnauthorizedError(); } diff --git a/src/routes/auth.ts b/src/routes/auth.ts index 484ac6a..c59e45c 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth.ts @@ -14,7 +14,7 @@ export default { }, changePassword: { ':id': { - PATCH: guard(auth.changePassword, [Claims.ADMIN, Claims.USERS.SELF.UPDATE]), + PATCH: guard(auth.changePassword, Claims.ADMIN, Claims.USERS.SELF.UPDATE), }, }, }; diff --git a/src/routes/circles.ts b/src/routes/circles.ts index f66f3ac..d140fc8 100644 --- a/src/routes/circles.ts +++ b/src/routes/circles.ts @@ -3,28 +3,30 @@ import { Claims } from '../orm/claims'; import circles from '../endpoints/circles'; export default { - 'POST': guard(circles.create, [Claims.ADMIN, Claims.CIRCLES.PUBLIC.CREATE, Claims.CIRCLES.PRIVATE.CREATE]), + 'POST': guard(circles.create, Claims.ADMIN, Claims.CIRCLES.PUBLIC.CREATE, Claims.CIRCLES.PRIVATE.CREATE), ':id': { - GET: guard(circles.get, [ + GET: guard( + circles.get, Claims.ADMIN, Claims.CIRCLES.PUBLIC.READ, Claims.CIRCLES.PRIVATE.READ, Claims.CIRCLES.PRIVATE.READ_IF_MEMBER, - ]), - PATCH: guard(circles.update, [Claims.ADMIN, Claims.CIRCLES.OWNED.UPDATE]), - DELETE: guard(circles.drop, [Claims.ADMIN, Claims.CIRCLES.OWNED.DELETE]), + ), + PATCH: guard(circles.update, Claims.ADMIN, Claims.CIRCLES.OWNED.UPDATE), + DELETE: guard(circles.drop, Claims.ADMIN, Claims.CIRCLES.OWNED.DELETE), invite: { - POST: guard(circles.invite, [ + POST: guard( + circles.invite, Claims.ADMIN, Claims.CIRCLES.PUBLIC.USERS.INVITE, Claims.CIRCLES.OWNED.USERS.INVITE, - ]), + ), }, }, 'search': { ':query': { variants: [':pageSize/:page', ':page'], - GET: guard(circles.query, [Claims.ADMIN, Claims.CIRCLES.PUBLIC.READ]), + GET: guard(circles.query, Claims.ADMIN, Claims.CIRCLES.PUBLIC.READ), }, }, }; diff --git a/src/routes/collections.ts b/src/routes/collections.ts index a030041..6c69760 100644 --- a/src/routes/collections.ts +++ b/src/routes/collections.ts @@ -3,26 +3,20 @@ import { Claims } from '../orm/claims'; import collections from '../endpoints/collections'; export default { - 'POST': guard(collections.create, [Claims.ADMIN, Claims.COLLECTIONS.CREATE]), + 'POST': guard(collections.create, Claims.ADMIN, Claims.COLLECTIONS.CREATE), ':id': { - GET: guard(collections.get, [Claims.ADMIN, Claims.COLLECTIONS.UNOWNED.READ, Claims.COLLECTIONS.OWNED.READ]), - PATCH: guard(collections.update, [Claims.ADMIN, Claims.PLAYERS.OTHER.UPDATE, Claims.PLAYERS.SELF.UPDATE]), - DELETE: guard(collections.drop, [Claims.ADMIN, Claims.PLAYERS.OTHER.DELETE, Claims.PLAYERS.SELF.DELETE]), + GET: guard(collections.get, Claims.ADMIN, Claims.COLLECTIONS.UNOWNED.READ, Claims.COLLECTIONS.OWNED.READ), + PATCH: guard(collections.update, Claims.ADMIN, Claims.PLAYERS.OTHER.UPDATE, Claims.PLAYERS.SELF.UPDATE), + DELETE: guard(collections.drop, Claims.ADMIN, Claims.PLAYERS.OTHER.DELETE, Claims.PLAYERS.SELF.DELETE), add: { - POST: guard(collections.addGame, [ - Claims.ADMIN, - Claims.COLLECTIONS.OWNED.GAME.ADD, - ]), + POST: guard(collections.addGame, Claims.ADMIN, Claims.COLLECTIONS.OWNED.GAME.ADD), }, remove: { - POST: guard(collections.removeGame, [ - Claims.ADMIN, - Claims.COLLECTIONS.OWNED.GAME.REMOVE, - ]), + POST: guard(collections.removeGame, Claims.ADMIN, Claims.COLLECTIONS.OWNED.GAME.REMOVE), }, }, 'list': { variants: [':pageSize/:page', ':page'], - GET: guard(collections.list, [Claims.ADMIN, Claims.COLLECTIONS.OWNED.LIST]), + GET: guard(collections.list, Claims.ADMIN, Claims.COLLECTIONS.OWNED.LIST), }, }; diff --git a/src/routes/games.ts b/src/routes/games.ts index cddcf3f..539373a 100644 --- a/src/routes/games.ts +++ b/src/routes/games.ts @@ -3,16 +3,16 @@ import { Claims } from '../orm/claims'; import games from '../endpoints/games'; export default { - 'POST': guard(games.create, [Claims.ADMIN, Claims.GAMES.CREATE]), + 'POST': guard(games.create, Claims.ADMIN, Claims.GAMES.CREATE), ':id': { - GET: guard(games.get, [Claims.ADMIN, Claims.GAMES.READ]), - PATCH: guard(games.update, [Claims.ADMIN, Claims.GAMES.UPDATE]), - DELETE: guard(games.drop, [Claims.ADMIN, Claims.GAMES.DELETE]), + GET: guard(games.get, Claims.ADMIN, Claims.GAMES.READ), + PATCH: guard(games.update, Claims.ADMIN, Claims.GAMES.UPDATE), + DELETE: guard(games.drop, Claims.ADMIN, Claims.GAMES.DELETE), }, 'search': { ':query': { variants: [':pageSize/:page', ':page'], - GET: guard(games.query, [Claims.ADMIN, Claims.GAMES.READ]), + GET: guard(games.query, Claims.ADMIN, Claims.GAMES.READ), }, }, }; diff --git a/src/routes/invites.ts b/src/routes/invites.ts index 24c6025..7c7b085 100644 --- a/src/routes/invites.ts +++ b/src/routes/invites.ts @@ -3,7 +3,7 @@ import { Claims } from '../orm/claims'; import invite from '../endpoints/invites'; export default { - POST: guard(invite.create, [Claims.ADMIN, Claims.USERS.INVITE]), + POST: guard(invite.create, Claims.ADMIN, Claims.USERS.INVITE), accept: { POST: unwrapMethod(invite.accept), }, diff --git a/src/routes/matches.ts b/src/routes/matches.ts index bee4b92..8fbe17e 100644 --- a/src/routes/matches.ts +++ b/src/routes/matches.ts @@ -3,12 +3,12 @@ import matches from '../endpoints/matches'; import { Claims } from '../orm/claims'; export default { - 'POST': guard(matches.create, [Claims.ADMIN, Claims.MATCHES.CREATE]), + 'POST': guard(matches.create, Claims.ADMIN, Claims.MATCHES.CREATE), ':id': { - GET: guard(matches.get, [Claims.ADMIN, Claims.MATCHES.OWNED.READ, Claims.MATCHES.PARTICIPANT.READ]), - DELETE: guard(matches.drop, [Claims.ADMIN, Claims.MATCHES.OWNED.DELETE, Claims.USERS.SELF.UPDATE]), + GET: guard(matches.get, Claims.ADMIN, Claims.MATCHES.OWNED.READ, Claims.MATCHES.PARTICIPANT.READ), + DELETE: guard(matches.drop, Claims.ADMIN, Claims.MATCHES.OWNED.DELETE, Claims.USERS.SELF.UPDATE), leave: { - POST: guard(matches.leave, [Claims.MATCHES.PARTICIPANT.LEAVE]), + POST: guard(matches.leave, Claims.MATCHES.PARTICIPANT.LEAVE), }, }, }; diff --git a/src/routes/players.ts b/src/routes/players.ts index 6e55c2d..e15cf69 100644 --- a/src/routes/players.ts +++ b/src/routes/players.ts @@ -3,14 +3,14 @@ import { Claims } from '../orm/claims'; import player from '../endpoints/players'; export default { - 'POST': guard(player.create, [Claims.ADMIN, Claims.PLAYERS.CREATE]), + 'POST': guard(player.create, Claims.ADMIN, Claims.PLAYERS.CREATE), ':id': { - GET: guard(player.get, [Claims.ADMIN, Claims.PLAYERS.OTHER.READ, Claims.PLAYERS.SELF.READ]), - PATCH: guard(player.update, [Claims.ADMIN, Claims.PLAYERS.OTHER.UPDATE, Claims.PLAYERS.SELF.UPDATE]), - DELETE: guard(player.drop, [Claims.ADMIN, Claims.PLAYERS.OTHER.DELETE, Claims.PLAYERS.SELF.DELETE]), + GET: guard(player.get, Claims.ADMIN, Claims.PLAYERS.OTHER.READ, Claims.PLAYERS.SELF.READ), + PATCH: guard(player.update, Claims.ADMIN, Claims.PLAYERS.OTHER.UPDATE, Claims.PLAYERS.SELF.UPDATE), + DELETE: guard(player.drop, Claims.ADMIN, Claims.PLAYERS.OTHER.DELETE, Claims.PLAYERS.SELF.DELETE), }, 'list': { variants: [':pageSize/:page', ':page'], - GET: guard(player.list, [Claims.ADMIN, Claims.PLAYERS.OTHER.READ]), + GET: guard(player.list, Claims.ADMIN, Claims.PLAYERS.OTHER.READ), }, }; diff --git a/src/routes/users.ts b/src/routes/users.ts index d05e732..0940378 100644 --- a/src/routes/users.ts +++ b/src/routes/users.ts @@ -3,10 +3,10 @@ import user from '../endpoints/users'; import { Claims } from '../orm/claims'; export default { - 'POST': guard(user.create, [Claims.ADMIN, Claims.USERS.CREATE]), + 'POST': guard(user.create, Claims.ADMIN, Claims.USERS.CREATE), ':id': { - GET: guard(user.get, [Claims.ADMIN, Claims.USERS.OTHER.READ, Claims.USERS.SELF.READ]), - PATCH: guard(user.update, [Claims.ADMIN, Claims.USERS.OTHER.UPDATE, Claims.USERS.SELF.UPDATE]), - DELETE: guard(user.drop, [Claims.ADMIN, Claims.USERS.OTHER.UPDATE, Claims.USERS.SELF.UPDATE]), + GET: guard(user.get, Claims.ADMIN, Claims.USERS.OTHER.READ, Claims.USERS.SELF.READ), + PATCH: guard(user.update, Claims.ADMIN, Claims.USERS.OTHER.UPDATE, Claims.USERS.SELF.UPDATE), + DELETE: guard(user.drop, Claims.ADMIN, Claims.USERS.OTHER.UPDATE, Claims.USERS.SELF.UPDATE), }, }; diff --git a/src/utilities/guard.ts b/src/utilities/guard.ts index 5694f10..9d2e36b 100644 --- a/src/utilities/guard.ts +++ b/src/utilities/guard.ts @@ -6,10 +6,10 @@ import { Claims } from '../orm/claims'; export function guardRedirect( method: (request: UnwrappedRequest) => Promise | Response, redirectMethod: Function, - guardedClaims: string[], + ...guardedClaims: string[] ) { try { - return guard(method, guardedClaims); + return guard(method, ...guardedClaims); } catch (e) { return redirectMethod(); } @@ -17,7 +17,7 @@ export function guardRedirect( export function guard( method: (request: UnwrappedRequest) => Promise | Response, - guardedClaims: string[], + ...guardedClaims: string[] ): (r: Request) => Promise { return async (request: Request): Promise => { const authHeader: string | null = @@ -26,10 +26,7 @@ export function guard( const userClaims: Claims = new Claims( jwt.verify(authHeader as string, process.env.JWT_SECRET_KEY as string) as any, ); - if ( - !userClaims.userId.raw || - !userClaims.claims.some((x: string): boolean => guardedClaims.includes(x)) - ) { + if (!userClaims.userId.raw || !userClaims.claims.some((x: string): boolean => guardedClaims.includes(x))) { return new UnauthorizedResponse('Unauthorized'); } return method(await unwrap(request, userClaims));