Minor tidy up and refactor to improve consistency.

This commit is contained in:
jd
2026-03-03 00:17:46 +00:00
parent c23536b3ed
commit c4a37f13be
24 changed files with 215 additions and 239 deletions

View File

@@ -33,9 +33,9 @@ async function login(request: UnwrappedRequest<LoginRequest>): Promise<Response>
httpOnly: true, httpOnly: true,
secure: true, secure: true,
maxAge: tokenLifeSpanInDays * 24 * 60 * 60, maxAge: tokenLifeSpanInDays * 24 * 60 * 60,
path: '/api/auth/token' path: '/api/auth/token',
}); });
return new OkResponse(); return new OkResponse({ token });
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }

View File

@@ -1,17 +1,12 @@
import { orm } from '../orm/orm'; import { orm } from '../orm/orm';
import { UnwrappedRequest } from '../utilities/guard'; import { UnwrappedRequest } from '../utilities/guard';
import { CreatedResponse, ErrorResponse, OkResponse, PagedResponse } from '../utilities/responseHelper'; import { CreatedResponse, ErrorResponse, OkResponse, PagedResponse } from '../utilities/responseHelper';
import { import { GameToCollectionRequest, CreateCollectionRequest, UpdateCollectionRequest } from '../utilities/requestModels';
GameToCollectionRequest,
CreateCollectionRequest,
UpdateCollectionRequest,
} from '../utilities/requestModels';
import { CollectionId, GameId } from '../utilities/secureIds'; import { CollectionId, GameId } from '../utilities/secureIds';
async function create(request: UnwrappedRequest<CreateCollectionRequest>): Promise<Response> { async function create(request: UnwrappedRequest<CreateCollectionRequest>): Promise<Response> {
try { try {
const newPlayer = await orm.collections.create(request.body, request.claims); return new CreatedResponse(await orm.collections.create(request.body, request.claims));
return new CreatedResponse(newPlayer);
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }

View File

@@ -1,23 +1,21 @@
import { orm } from '../orm/orm'; import { orm } from '../orm/orm';
import { UnwrappedRequest } from '../utilities/guard'; import { UnwrappedRequest } from '../utilities/guard';
import { CreatedResponse, ErrorResponse, OkResponse, PagedResponse } from '../utilities/responseHelper'; import { CreatedResponse, ErrorResponse, OkResponse, PagedResponse } from '../utilities/responseHelper';
import { import { CreateGameRequest, UpdateGameRequest } from '../utilities/requestModels';
CreateGameRequest,
UpdateGameRequest,
} from '../utilities/requestModels';
import { GameId } from '../utilities/secureIds'; import { GameId } from '../utilities/secureIds';
async function create(request: UnwrappedRequest<CreateGameRequest>): Promise<Response> { async function create(request: UnwrappedRequest<CreateGameRequest>): Promise<Response> {
try { try {
const newUser = await orm.games.create( return new CreatedResponse(
await orm.games.create(
{ {
name: request.body.name, name: request.body.name,
bggId: request.body.bggId, bggId: request.body.bggId,
imagePath: request.body.imagePath, imagePath: request.body.imagePath,
}, },
request.claims, request.claims,
),
); );
return new CreatedResponse(newUser);
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }
@@ -33,13 +31,7 @@ async function get(request: UnwrappedRequest): Promise<Response> {
async function update(request: UnwrappedRequest<UpdateGameRequest>): Promise<Response> { async function update(request: UnwrappedRequest<UpdateGameRequest>): Promise<Response> {
try { try {
return new OkResponse( return new OkResponse(await orm.games.update(GameId.fromHash(request.params.id), request.body, request.claims));
await orm.games.update(
GameId.fromHash(request.params.id),
request.body,
request.claims,
),
);
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }

View File

@@ -1,22 +1,18 @@
import { orm } from '../orm/orm'; import { orm } from '../orm/orm';
import { UnwrappedRequest } from '../utilities/guard'; import { UnwrappedRequest } from '../utilities/guard';
import { CreatedResponse, ErrorResponse } from '../utilities/responseHelper'; import { CreatedResponse, ErrorResponse } from '../utilities/responseHelper';
import { import { AcceptInviteRequest, InviteUserRequest } from '../utilities/requestModels';
AcceptInviteRequest,
InviteUserRequest,
} from '../utilities/requestModels';
import { PlayerId, UserId } from '../utilities/secureIds'; import { PlayerId, UserId } from '../utilities/secureIds';
async function create(request: UnwrappedRequest<InviteUserRequest>): Promise<Response> { async function create(request: UnwrappedRequest<InviteUserRequest>): Promise<Response> {
try { try {
const newUser = await orm.invites.create( return new CreatedResponse(
{ await orm.invites.create({
...request.body, ...request.body,
playerId: PlayerId.fromHash(request.body.playerId), playerId: PlayerId.fromHash(request.body.playerId),
invitedByUserId: request.claims.userId as UserId, invitedByUserId: request.claims.userId as UserId,
}, }),
); );
return new CreatedResponse(newUser);
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }
@@ -24,8 +20,7 @@ async function create(request: UnwrappedRequest<InviteUserRequest>): Promise<Res
async function accept(request: UnwrappedRequest<AcceptInviteRequest>): Promise<Response> { async function accept(request: UnwrappedRequest<AcceptInviteRequest>): Promise<Response> {
try { try {
const newUser = await orm.invites.accept(request.body); return new CreatedResponse(await orm.invites.accept(request.body));
return new CreatedResponse(newUser);
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }

View File

@@ -7,14 +7,15 @@ import { MatchParticipant } from '../orm/matches';
async function create(request: UnwrappedRequest<CreateMatchRequest>): Promise<Response> { async function create(request: UnwrappedRequest<CreateMatchRequest>): Promise<Response> {
try { try {
const newUser = await orm.matches.create({ return new CreatedResponse(
await orm.matches.create({
gameId: GameId.fromHash(request.body.gameId), gameId: GameId.fromHash(request.body.gameId),
ownerId: request.claims.userId as UserId, ownerId: request.claims.userId as UserId,
participants: request.body.participants.map( participants: request.body.participants.map(
(x) => new MatchParticipant(PlayerId.fromHash(x.playerId), x.standing), (x) => new MatchParticipant(PlayerId.fromHash(x.playerId), x.standing),
), ),
}); }),
return new CreatedResponse(newUser); );
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }

View File

@@ -6,8 +6,7 @@ import { PlayerId } from '../utilities/secureIds';
async function create(request: UnwrappedRequest<CreatePlayerRequest>): Promise<Response> { async function create(request: UnwrappedRequest<CreatePlayerRequest>): Promise<Response> {
try { try {
const newPlayer = await orm.players.create(request.body); return new CreatedResponse(await orm.players.create(request.body));
return new CreatedResponse(newPlayer);
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }

View File

@@ -6,13 +6,12 @@ import { PlayerId, UserId } from '../utilities/secureIds';
async function create(request: UnwrappedRequest<CreateUserRequest>): Promise<Response> { async function create(request: UnwrappedRequest<CreateUserRequest>): Promise<Response> {
try { try {
const newUser = await orm.users.create( return new CreatedResponse(
{ await orm.users.create({
...request.body, ...request.body,
playerId: PlayerId.fromHash(request.body.playerId), playerId: PlayerId.fromHash(request.body.playerId),
} }),
); );
return new CreatedResponse(newUser);
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }
@@ -28,9 +27,7 @@ async function get(request: UnwrappedRequest): Promise<Response> {
async function update(request: UnwrappedRequest<UpdateUserRequest>): Promise<Response> { async function update(request: UnwrappedRequest<UpdateUserRequest>): Promise<Response> {
try { try {
return new OkResponse( return new OkResponse(await orm.users.update(UserId.fromHash(request.params.id), request.body, request.claims));
await orm.users.update(UserId.fromHash(request.params.id), request.body, request.claims),
);
} catch (error: any) { } catch (error: any) {
return new ErrorResponse(error as Error); return new ErrorResponse(error as Error);
} }

View File

@@ -51,20 +51,20 @@ export class CircleOrm {
${model.name}, ${model.name},
${model.isPublic}, ${model.isPublic},
${model.colour})`; ${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<Circle> { async get(id: CircleId, claims?: Claims): Promise<Circle> {
const circleResult: any = first( const record: any = first(
await sql`SELECT * await sql`SELECT *
FROM circles FROM circles
WHERE id = ${id.raw} WHERE id = ${id.raw}
LIMIT 1`, LIMIT 1`,
); );
if (!circleResult) { if (!record) {
throw new NotFoundError('No matching game exists'); throw new NotFoundError('No matching game exists');
} }
@@ -72,8 +72,8 @@ export class CircleOrm {
if ( if (
claims && claims &&
!claims.test(Claims.ADMIN) && !claims.test(Claims.ADMIN) &&
!(claims.test(Claims.CIRCLES.PUBLIC.READ) && circleResult.is_public) && !(claims.test(Claims.CIRCLES.PUBLIC.READ) && record.is_public) &&
!(claims.test(Claims.CIRCLES.OWNED.READ) && circleResult.owning_user_id === claims.userId.raw) && !(claims.test(Claims.CIRCLES.OWNED.READ) && record.owning_user_id === claims.userId.raw) &&
!( !(
claims.test(Claims.CIRCLES.PRIVATE.READ_IF_MEMBER) && claims.test(Claims.CIRCLES.PRIVATE.READ_IF_MEMBER) &&
(user = await orm.users.get(claims.userId)) && (user = await orm.users.get(claims.userId)) &&
@@ -86,25 +86,25 @@ export class CircleOrm {
} }
return new Circle({ return new Circle({
id: CircleId.fromID(circleResult.id), id: CircleId.fromID(record.id),
owningUserId: UserId.fromID(circleResult.owning_user_id), owningUserId: UserId.fromID(record.owning_user_id),
name: circleResult.name, name: record.name,
isPublic: circleResult.is_public, isPublic: record.is_public,
imagePath: circleResult.image_path, imagePath: record.image_path,
colour: circleResult.colour, colour: record.colour,
}); });
} }
async update(id: CircleId, patch: UpdateCircleRequest): Promise<Circle> { async update(id: CircleId, patch: UpdateCircleRequest): Promise<Circle> {
const recordToUpdate = await this.get(id); const circle = await this.get(id);
recordToUpdate.name = patch.name ?? recordToUpdate.name; circle.name = patch.name ?? circle.name;
recordToUpdate.colour = patch.colour ?? recordToUpdate.colour; circle.colour = patch.colour ?? circle.colour;
recordToUpdate.imagePath = patch.imagePath ?? recordToUpdate.imagePath; circle.imagePath = patch.imagePath ?? circle.imagePath;
await sql`UPDATE circles await sql`UPDATE circles
SET name=${recordToUpdate.name}, SET name=${circle.name},
colour=${recordToUpdate.colour}, colour=${circle.colour},
image_path=${recordToUpdate.imagePath} image_path=${circle.imagePath}
WHERE id = ${id.raw}`; WHERE id = ${id.raw}`;
return await this.get(id); return await this.get(id);
@@ -131,8 +131,12 @@ export class CircleOrm {
return; return;
} }
async invite(circleId: CircleId, relatedRecord: PlayerId | UserId | string | undefined, claims: Claims): Promise<void> { async invite(
if(relatedRecord === undefined) { circleId: CircleId,
relatedRecord: PlayerId | UserId | string | undefined,
claims: Claims,
): Promise<void> {
if (relatedRecord === undefined) {
throw new BadRequestError(); throw new BadRequestError();
} }
@@ -164,18 +168,18 @@ export class CircleOrm {
query: (query: string) => Promise<Circle[]> = memo<(query: string) => Promise<Circle[]>, Circle[]>(this.#query); query: (query: string) => Promise<Circle[]> = memo<(query: string) => Promise<Circle[]>, Circle[]>(this.#query);
async #query(query: string): Promise<Circle[]> { async #query(query: string): Promise<Circle[]> {
const dbResult: any = await sql` SELECT const queryResult: any = await sql` SELECT
id, name, owning_user_id, is_public id, name, owning_user_id, is_public
FROM (SELECT *, SIMILARITY(${query}, name) as similarity FROM circles WHERE is_public=true) FROM (SELECT *, SIMILARITY(${query}, name) as similarity FROM circles WHERE is_public=true)
WHERE similarity > 0 WHERE similarity > 0
ORDER BY similarity ORDER BY similarity
LIMIT 5;`; LIMIT 5;`;
if (!dbResult) { if (!queryResult) {
throw new NotFoundError('No matching circles exists'); 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 }) => (x: { id: string; name: string; owning_user_id: string; is_public: boolean }) =>
new Circle({ new Circle({
id: CircleId.fromID(x.id), id: CircleId.fromID(x.id),

View File

@@ -8,8 +8,8 @@ export class Claims extends ClaimDefinition {
constructor(raw?: { userId?: string | UserId; claims?: string[] }) { constructor(raw?: { userId?: string | UserId; claims?: string[] }) {
super(); super();
if(raw?.userId instanceof UserId) { if (raw?.userId instanceof UserId) {
this.userId = raw.userId this.userId = raw.userId;
} else { } else {
this.userId = UserId.fromHash(raw?.userId ?? ''); this.userId = UserId.fromHash(raw?.userId ?? '');
} }
@@ -30,20 +30,20 @@ export class Claims extends ClaimDefinition {
export class ClaimsOrm { export class ClaimsOrm {
async getByUserId(userId: UserId): Promise<Claims> { async getByUserId(userId: UserId): Promise<Claims> {
const dbResults: any[] = await sql`SELECT c.name const records: any[] = await sql`SELECT c.name
from user_claims as uc from user_claims as uc
JOIN claims as c on uc.claim_id = c.id JOIN claims as c on uc.claim_id = c.id
where uc.user_id = ${userId.raw};`; where uc.user_id = ${userId.raw};`;
return new Claims({ return new Claims({
userId: userId, userId: userId,
claims: dbResults.map((x) => x.name), claims: records.map((x) => x.name),
}); });
} }
async getDefaultClaims(): Promise<number[]> { async getDefaultClaims(): Promise<number[]> {
const dbResults: any[] = await sql`SELECT id const records: any[] = await sql`SELECT id
FROM claims FROM claims
WHERE is_default = true;`; WHERE is_default = true;`;
return dbResults.map((x) => x.id); return records.map((x) => x.id);
} }
} }

View File

@@ -24,13 +24,13 @@ export class CollectionsOrm {
async create(model: { name: string }, claims: Claims): Promise<Collection> { async create(model: { name: string }, claims: Claims): Promise<Collection> {
await sql`INSERT INTO collections (name, user_id) await sql`INSERT INTO collections (name, user_id)
VALUES (${model.name}, ${claims?.userId?.raw})`; 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<Collection> { async get(id: CollectionId, claims?: Claims): Promise<Collection> {
const dbResult: any = await sql`SELECT const records: any = await sql`SELECT
c.id AS collection_id, c.id AS collection_id,
c.name AS collection_name, c.name AS collection_name,
c.user_id AS user_id, c.user_id AS user_id,
@@ -45,21 +45,21 @@ export class CollectionsOrm {
!( !(
claims.test(Claims.ADMIN, Claims.COLLECTIONS.UNOWNED.READ) || claims.test(Claims.ADMIN, Claims.COLLECTIONS.UNOWNED.READ) ||
(Claims.test(claims, Claims.COLLECTIONS.OWNED.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(); throw new UnauthorizedError();
} }
if (!dbResult?.length) { if (!records?.length) {
throw new NotFoundError('No matching player exists'); throw new NotFoundError('No matching player exists');
} }
return new Collection({ return new Collection({
id: CollectionId.fromID(dbResult[0].collection_id), id: CollectionId.fromID(records[0].collection_id),
name: dbResult[0].collection_name, name: records[0].collection_name,
ownerId: UserId.fromID(dbResult[0].user_id), ownerId: UserId.fromID(records[0].user_id),
games: dbResult games: records
.filter((x: { game_id: string; game_name: string }) => x.game_id) .filter((x: { game_id: string; game_name: string }) => x.game_id)
.map( .map(
(x: { game_id: string; game_name: string }) => (x: { game_id: string; game_name: string }) =>

View File

@@ -24,44 +24,44 @@ export class GamesOrm {
async create(model: CreateGameRequest, claims?: Claims): Promise<Game> { async create(model: CreateGameRequest, claims?: Claims): Promise<Game> {
await sql`INSERT INTO games (name, image_path, bgg_id) await sql`INSERT INTO games (name, image_path, bgg_id)
VALUES (${model.name}, ${claims?.test(Claims.GAMES.MANAGE_IMAGES) ? model.imagePath : null}, ${model.bggId})`; 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<Game> { async get(id: GameId): Promise<Game> {
const dbResult: any = first( const record: any = first(
await sql`SELECT * await sql`SELECT *
FROM games FROM games
WHERE id = ${id.raw} WHERE id = ${id.raw}
LIMIT 1`, LIMIT 1`,
); );
if (!dbResult) { if (!record) {
throw new NotFoundError('No matching game exists'); throw new NotFoundError('No matching game exists');
} }
return new Game({ return new Game({
id: GameId.fromID(dbResult.id), id: GameId.fromID(record.id),
name: dbResult.name, name: record.name,
bggId: dbResult.bgg_id, bggId: record.bgg_id,
imagePath: dbResult.image_path, imagePath: record.image_path,
}); });
} }
async update(id: GameId, patch: UpdateGameRequest, claims?: Claims): Promise<Game> { async update(id: GameId, patch: UpdateGameRequest, claims?: Claims): Promise<Game> {
const gameToUpdate = await this.get(id); const game = await this.get(id);
gameToUpdate.name = patch.name ?? gameToUpdate.name; game.name = patch.name ?? game.name;
gameToUpdate.bggId = patch.bggId ?? gameToUpdate.bggId; game.bggId = patch.bggId ?? game.bggId;
if (claims?.test(Claims.GAMES.MANAGE_IMAGES)) { if (claims?.test(Claims.GAMES.MANAGE_IMAGES)) {
gameToUpdate.imagePath = patch.imagePath ?? gameToUpdate.imagePath; game.imagePath = patch.imagePath ?? game.imagePath;
} }
await sql`UPDATE games await sql`UPDATE games
SET name=${gameToUpdate.name}, SET name=${game.name},
bgg_id=${gameToUpdate.bggId}, bgg_id=${game.bggId},
image_path=${gameToUpdate.imagePath} image_path=${game.imagePath}
WHERE id = ${id.raw}`; WHERE id = ${id.raw}`;
return await this.get(id); return await this.get(id);
@@ -90,18 +90,18 @@ export class GamesOrm {
query: (query: string) => Promise<Game[]> = memo<(query: string) => Promise<Game[]>, Game[]>(this.#query); query: (query: string) => Promise<Game[]> = memo<(query: string) => Promise<Game[]>, Game[]>(this.#query);
async #query(query: string): Promise<Game[]> { async #query(query: string): Promise<Game[]> {
const dbResult: any = await sql` SELECT const records: any = await sql` SELECT
id, name id, name
FROM (SELECT *, SIMILARITY(${query}, name) as similarity FROM games) FROM (SELECT *, SIMILARITY(${query}, name) as similarity FROM games)
WHERE similarity > 0 WHERE similarity > 0
ORDER BY similarity ORDER BY similarity
LIMIT 5;`; LIMIT 5;`;
if (!dbResult) { if (!records) {
throw new NotFoundError('No matching game exists'); throw new NotFoundError('No matching game exists');
} }
return dbResult.map( return records.map(
(x: { id: string; name: string }) => (x: { id: string; name: string }) =>
new Game({ new Game({
id: GameId.fromID(x.id), id: GameId.fromID(x.id),

View File

@@ -35,7 +35,7 @@ export class InvitesOrm {
throw new UnauthorizedError('Invite allowance reached.'); throw new UnauthorizedError('Invite allowance reached.');
} }
const inviteExists = ( const doesInviteExist = (
first( first(
await sql`SELECT COUNT(*) > 0 AS exists await sql`SELECT COUNT(*) > 0 AS exists
FROM user_invites FROM user_invites
@@ -45,12 +45,12 @@ export class InvitesOrm {
exists: boolean; exists: boolean;
} }
)?.exists; )?.exists;
if (inviteExists) { if (doesInviteExist) {
throw new BadRequestError('Player has already been invited.'); throw new BadRequestError('Player has already been invited.');
} }
} }
const playerHasUser = ( const isPlayerAssociatedWithUser = (
first( first(
await sql`SELECT COUNT(*) > 0 AS exists await sql`SELECT COUNT(*) > 0 AS exists
FROM users FROM users
@@ -60,7 +60,7 @@ export class InvitesOrm {
exists: boolean; exists: boolean;
} }
)?.exists; )?.exists;
if (playerHasUser) { if (isPlayerAssociatedWithUser) {
throw new BadRequestError('User has already been invited.'); throw new BadRequestError('User has already been invited.');
} }
@@ -69,7 +69,7 @@ export class InvitesOrm {
const invitationCode = createRandomString(6); const invitationCode = createRandomString(6);
await sql`INSERT INTO user_invites (invite_code, email, player_id, invited_by_user_id) await sql`INSERT INTO user_invites (invite_code, email, player_id, invited_by_user_id)
VALUES (${invitationCode}, ${email}, ${playerId.raw}, ${invitedByUserId.raw})`; 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 resend = new Resend(process.env.RESEND_KEY);
const resendResponse = await resend.emails.send({ const resendResponse = await resend.emails.send({
@@ -83,51 +83,51 @@ export class InvitesOrm {
throw new InternalServerError(); 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; return;
} }
async accept({ inviteCode, password }: { inviteCode: string; password: string }): Promise<User> { async accept({ inviteCode, password }: { inviteCode: string; password: string }): Promise<User> {
const invite: { const record: {
id: string; id: string;
email: string; email: string;
player_id: string; player_id: string;
accepted: boolean; accepted: boolean;
} = first(await sql`SELECT * FROM user_invites WHERE invite_code=${inviteCode} LIMIT 1`); } = first(await sql`SELECT * FROM user_invites WHERE invite_code=${inviteCode} LIMIT 1`);
if (!invite) { if (!record) {
throw new NotFoundError('Invalid invite code'); throw new NotFoundError('Invalid invite code');
} }
if (invite.accepted) { if (record.accepted) {
throw new UnauthorizedError('Invite already accepted'); throw new UnauthorizedError('Invite already accepted');
} }
const playerHasUser = ( const isPlayerAssociatedWithUser = (
first( first(
await sql`SELECT COUNT(*) > 0 AS exists await sql`SELECT COUNT(*) > 0 AS exists
FROM users FROM users
WHERE player_id = ${invite.player_id} WHERE player_id = ${record.player_id}
OR email = ${invite.email}`, OR email = ${record.email}`,
) as { ) as {
exists: boolean; exists: boolean;
} }
)?.exists; )?.exists;
if (playerHasUser) { if (isPlayerAssociatedWithUser) {
throw new BadRequestError('User has already been invited.'); throw new BadRequestError('User has already been invited.');
} }
const createdUser = await orm.users.create({ const user = await orm.users.create({
email: invite.email, email: record.email,
playerId: PlayerId.fromID(invite.player_id), playerId: PlayerId.fromID(record.player_id),
password, password,
}); });
await sql`UPDATE user_invites await sql`UPDATE user_invites
SET accepted = true SET accepted = true
WHERE id = ${invite.id}`; WHERE id = ${record.id}`;
return createdUser; return user;
} }
} }

View File

@@ -176,10 +176,11 @@ export class MatchOrm {
return; return;
} }
async removePlayer(matchId: MatchId, participantId: UserId): Promise<void>; async removePlayer(matchId: MatchId, participantId: UserId | PlayerId): Promise<void> {
async removePlayer(matchId: MatchId, participantId: PlayerId): Promise<void> { let playerId: PlayerId;
let playerId: PlayerId = participantId; if (participantId instanceof PlayerId) {
if (participantId instanceof UserId) { playerId = participantId;
} else {
playerId = (await orm.users.get(participantId))?.playerId; playerId = (await orm.users.get(participantId))?.playerId;
if (!playerId) { if (!playerId) {
throw new BadRequestError('User is not a participant'); throw new BadRequestError('User is not a participant');

View File

@@ -32,9 +32,9 @@ export class PlayersOrm {
async create(model: { name: string }): Promise<Player> { async create(model: { name: string }): Promise<Player> {
await sql`INSERT INTO players (name) await sql`INSERT INTO players (name)
VALUES (${model.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<Player> { async get(id: PlayerId, claims?: Claims): Promise<Player> {
@@ -47,23 +47,23 @@ export class PlayersOrm {
} }
} }
const dbResult: any = first( const record: any = first(
await sql`SELECT * await sql`SELECT *
FROM players FROM players
WHERE id = ${id.raw} WHERE id = ${id.raw}
LIMIT 1`, LIMIT 1`,
); );
if (!dbResult) { if (!record) {
throw new NotFoundError('No matching player exists'); throw new NotFoundError('No matching player exists');
} }
return new Player({ return new Player({
id: PlayerId.fromID(dbResult.id), id: PlayerId.fromID(record.id),
name: dbResult.name, name: record.name,
elo: parseInt(dbResult.elo), elo: parseInt(record.elo),
isRatingLocked: dbResult.is_rating_locked, isRatingLocked: record.is_rating_locked,
canBeMultiple: dbResult.can_be_multiple, canBeMultiple: record.can_be_multiple,
}); });
} }
@@ -119,15 +119,15 @@ export class PlayersOrm {
} }
} }
const playerToUpdate = await this.get(id); const player = await this.get(id);
playerToUpdate.name = patch.name ?? playerToUpdate.name; player.name = patch.name ?? player.name;
playerToUpdate.isRatingLocked = patch.isRatingLocked ?? playerToUpdate.isRatingLocked; player.isRatingLocked = patch.isRatingLocked ?? player.isRatingLocked;
playerToUpdate.canBeMultiple = patch.canBeMultiple ?? playerToUpdate.canBeMultiple; player.canBeMultiple = patch.canBeMultiple ?? player.canBeMultiple;
await sql`UPDATE players await sql`UPDATE players
SET name=${playerToUpdate.name}, SET name=${player.name},
is_rating_locked=${playerToUpdate.isRatingLocked}, is_rating_locked=${player.isRatingLocked},
can_be_multiple=${playerToUpdate.canBeMultiple} can_be_multiple=${player.canBeMultiple}
WHERE id = ${id.raw}`; WHERE id = ${id.raw}`;
return await this.get(id); return await this.get(id);

View File

@@ -47,15 +47,15 @@ export class UsersOrm {
const passwordHash = await argon2.hash(password); const passwordHash = await argon2.hash(password);
await sql`INSERT INTO users (email, pass_hash, player_id) await sql`INSERT INTO users (email, pass_hash, player_id)
VALUES (${email}, ${passwordHash}, ${playerId.raw})`; 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) => { await sql.transaction(async (tx) => {
for (let i in defaultClaims) { for (let i in defaultClaims) {
await tx`INSERT INTO user_claims (user_id, claim_id) 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<User> { async get(id: UserId, claims?: Claims): Promise<User> {
@@ -69,7 +69,7 @@ export class UsersOrm {
throw new UnauthorizedError(); throw new UnauthorizedError();
} }
const dbResult: any = first( const record: any = first(
await sql`SELECT * await sql`SELECT *
FROM users FROM users
WHERE id = ${id.raw} WHERE id = ${id.raw}
@@ -77,21 +77,20 @@ export class UsersOrm {
LIMIT 1`, LIMIT 1`,
); );
if (!dbResult) { if (!record) {
throw new NotFoundError('No matching user exists'); throw new NotFoundError('No matching user exists');
} }
return new User( return new User(
UserId.fromID(dbResult.id), UserId.fromID(record.id),
PlayerId.fromID(dbResult.player_id), PlayerId.fromID(record.player_id),
dbResult.email, record.email,
dbResult.is_admin, record.is_admin,
); );
} }
async getByPlayer(id: PlayerId, claims?: Claims): Promise<User> { async getByPlayer(id: PlayerId, claims?: Claims): Promise<User> {
const record: any = first(
const dbResult: any = first(
await sql`SELECT * await sql`SELECT *
FROM users FROM users
WHERE player_id = ${id.raw} WHERE player_id = ${id.raw}
@@ -99,7 +98,7 @@ export class UsersOrm {
LIMIT 1`, LIMIT 1`,
); );
if (!dbResult) { if (!record) {
throw new NotFoundError('No matching user exists'); throw new NotFoundError('No matching user exists');
} }
@@ -107,22 +106,22 @@ export class UsersOrm {
claims && claims &&
!( !(
claims.test(Claims.ADMIN, Claims.USERS.OTHER.READ) || 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(); throw new UnauthorizedError();
} }
return new User( return new User(
UserId.fromID(dbResult.id), UserId.fromID(record.id),
PlayerId.fromID(dbResult.player_id), PlayerId.fromID(record.player_id),
dbResult.email, record.email,
dbResult.is_admin, record.is_admin,
); );
} }
async getByEmail(email:string, claims?: Claims): Promise<User> { async getByEmail(email: string, claims?: Claims): Promise<User> {
const dbResult: any = first( const record: any = first(
await sql`SELECT * await sql`SELECT *
FROM users FROM users
WHERE email = ${email} WHERE email = ${email}
@@ -130,7 +129,7 @@ export class UsersOrm {
LIMIT 1`, LIMIT 1`,
); );
if (!dbResult) { if (!record) {
throw new NotFoundError('No matching user exists'); throw new NotFoundError('No matching user exists');
} }
@@ -138,17 +137,17 @@ export class UsersOrm {
claims && claims &&
!( !(
claims.test(Claims.ADMIN, Claims.USERS.OTHER.READ) || 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(); throw new UnauthorizedError();
} }
return new User( return new User(
UserId.fromID(dbResult.id), UserId.fromID(record.id),
PlayerId.fromID(dbResult.player_id), PlayerId.fromID(record.player_id),
dbResult.email, record.email,
dbResult.is_admin, record.is_admin,
); );
} }
@@ -163,15 +162,15 @@ export class UsersOrm {
throw new UnauthorizedError(); throw new UnauthorizedError();
} }
const userToUpdate = await this.get(id); const user = await this.get(id);
if (!claims || claims.test(Claims.ADMIN)) { if (!claims || claims.test(Claims.ADMIN)) {
userToUpdate.isActive = patch.isActive ?? userToUpdate.isActive; user.isActive = patch.isActive ?? user.isActive;
userToUpdate.isAdmin = patch.isAdmin ?? userToUpdate.isAdmin; user.isAdmin = patch.isAdmin ?? user.isAdmin;
} }
await sql`UPDATE users await sql`UPDATE users
SET is_active=${userToUpdate.isActive}, SET is_active=${user.isActive},
is_admin=${userToUpdate.isAdmin} is_admin=${user.isAdmin}
WHERE id = ${id.raw}`; WHERE id = ${id.raw}`;
return await this.get(id); return await this.get(id);
@@ -203,35 +202,35 @@ export class UsersOrm {
} }
async verifyCredentials(email: string, password: string): Promise<{ userId: UserId; refreshCount: string } | null> { async verifyCredentials(email: string, password: string): Promise<{ userId: UserId; refreshCount: string } | null> {
const dbResult: any = first( const record: any = first(
await sql`SELECT * await sql`SELECT *
FROM users FROM users
WHERE email = ${email} WHERE email = ${email}
AND is_active = true AND is_active = true
limit 1`, limit 1`,
); );
if (!dbResult) { if (!record) {
throw new UnauthorizedError(); throw new UnauthorizedError();
} }
if (!(await argon2.verify(dbResult.pass_hash, password))) { if (!(await argon2.verify(record.pass_hash, password))) {
return null; return null;
} }
return { return {
userId: UserId.fromID(dbResult.id), userId: UserId.fromID(record.id),
refreshCount: dbResult.refresh_count, refreshCount: record.refresh_count,
}; };
} }
async verifyRefreshCount(id: UserId, refreshCount: string): Promise<boolean> { async verifyRefreshCount(id: UserId, refreshCount: string): Promise<boolean> {
const dbResult: any = first( const record: any = first(
await sql`SELECT * await sql`SELECT *
FROM users FROM users
WHERE id = ${id.raw} WHERE id = ${id.raw}
LIMIT 1`, LIMIT 1`,
); );
return dbResult.refresh_count === refreshCount; return record.refresh_count === refreshCount;
} }
async changePassword(id: UserId, oldPassword: string | null, newPassword: string, claims?: Claims): Promise<void> { async changePassword(id: UserId, oldPassword: string | null, newPassword: string, claims?: Claims): Promise<void> {
@@ -244,14 +243,14 @@ export class UsersOrm {
throw new BadRequestError('Password is required'); throw new BadRequestError('Password is required');
} }
const dbUser: any = first( const record: any = first(
await sql`SELECT * await sql`SELECT *
FROM users FROM users
WHERE id = ${id.raw} WHERE id = ${id.raw}
LIMIT 1`, 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(); throw new UnauthorizedError();
} }

View File

@@ -14,7 +14,7 @@ export default {
}, },
changePassword: { changePassword: {
':id': { ':id': {
PATCH: guard(auth.changePassword, [Claims.ADMIN, Claims.USERS.SELF.UPDATE]), PATCH: guard(auth.changePassword, Claims.ADMIN, Claims.USERS.SELF.UPDATE),
}, },
}, },
}; };

View File

@@ -3,28 +3,30 @@ import { Claims } from '../orm/claims';
import circles from '../endpoints/circles'; import circles from '../endpoints/circles';
export default { 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': { ':id': {
GET: guard(circles.get, [ GET: guard(
circles.get,
Claims.ADMIN, Claims.ADMIN,
Claims.CIRCLES.PUBLIC.READ, Claims.CIRCLES.PUBLIC.READ,
Claims.CIRCLES.PRIVATE.READ, Claims.CIRCLES.PRIVATE.READ,
Claims.CIRCLES.PRIVATE.READ_IF_MEMBER, Claims.CIRCLES.PRIVATE.READ_IF_MEMBER,
]), ),
PATCH: guard(circles.update, [Claims.ADMIN, Claims.CIRCLES.OWNED.UPDATE]), PATCH: guard(circles.update, Claims.ADMIN, Claims.CIRCLES.OWNED.UPDATE),
DELETE: guard(circles.drop, [Claims.ADMIN, Claims.CIRCLES.OWNED.DELETE]), DELETE: guard(circles.drop, Claims.ADMIN, Claims.CIRCLES.OWNED.DELETE),
invite: { invite: {
POST: guard(circles.invite, [ POST: guard(
circles.invite,
Claims.ADMIN, Claims.ADMIN,
Claims.CIRCLES.PUBLIC.USERS.INVITE, Claims.CIRCLES.PUBLIC.USERS.INVITE,
Claims.CIRCLES.OWNED.USERS.INVITE, Claims.CIRCLES.OWNED.USERS.INVITE,
]), ),
}, },
}, },
'search': { 'search': {
':query': { ':query': {
variants: [':pageSize/:page', ':page'], variants: [':pageSize/:page', ':page'],
GET: guard(circles.query, [Claims.ADMIN, Claims.CIRCLES.PUBLIC.READ]), GET: guard(circles.query, Claims.ADMIN, Claims.CIRCLES.PUBLIC.READ),
}, },
}, },
}; };

View File

@@ -3,26 +3,20 @@ import { Claims } from '../orm/claims';
import collections from '../endpoints/collections'; import collections from '../endpoints/collections';
export default { export default {
'POST': guard(collections.create, [Claims.ADMIN, Claims.COLLECTIONS.CREATE]), 'POST': guard(collections.create, Claims.ADMIN, Claims.COLLECTIONS.CREATE),
':id': { ':id': {
GET: guard(collections.get, [Claims.ADMIN, Claims.COLLECTIONS.UNOWNED.READ, Claims.COLLECTIONS.OWNED.READ]), 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]), 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]), DELETE: guard(collections.drop, Claims.ADMIN, Claims.PLAYERS.OTHER.DELETE, Claims.PLAYERS.SELF.DELETE),
add: { add: {
POST: guard(collections.addGame, [ POST: guard(collections.addGame, Claims.ADMIN, Claims.COLLECTIONS.OWNED.GAME.ADD),
Claims.ADMIN,
Claims.COLLECTIONS.OWNED.GAME.ADD,
]),
}, },
remove: { remove: {
POST: guard(collections.removeGame, [ POST: guard(collections.removeGame, Claims.ADMIN, Claims.COLLECTIONS.OWNED.GAME.REMOVE),
Claims.ADMIN,
Claims.COLLECTIONS.OWNED.GAME.REMOVE,
]),
}, },
}, },
'list': { 'list': {
variants: [':pageSize/:page', ':page'], variants: [':pageSize/:page', ':page'],
GET: guard(collections.list, [Claims.ADMIN, Claims.COLLECTIONS.OWNED.LIST]), GET: guard(collections.list, Claims.ADMIN, Claims.COLLECTIONS.OWNED.LIST),
}, },
}; };

View File

@@ -3,16 +3,16 @@ import { Claims } from '../orm/claims';
import games from '../endpoints/games'; import games from '../endpoints/games';
export default { export default {
'POST': guard(games.create, [Claims.ADMIN, Claims.GAMES.CREATE]), 'POST': guard(games.create, Claims.ADMIN, Claims.GAMES.CREATE),
':id': { ':id': {
GET: guard(games.get, [Claims.ADMIN, Claims.GAMES.READ]), GET: guard(games.get, Claims.ADMIN, Claims.GAMES.READ),
PATCH: guard(games.update, [Claims.ADMIN, Claims.GAMES.UPDATE]), PATCH: guard(games.update, Claims.ADMIN, Claims.GAMES.UPDATE),
DELETE: guard(games.drop, [Claims.ADMIN, Claims.GAMES.DELETE]), DELETE: guard(games.drop, Claims.ADMIN, Claims.GAMES.DELETE),
}, },
'search': { 'search': {
':query': { ':query': {
variants: [':pageSize/:page', ':page'], variants: [':pageSize/:page', ':page'],
GET: guard(games.query, [Claims.ADMIN, Claims.GAMES.READ]), GET: guard(games.query, Claims.ADMIN, Claims.GAMES.READ),
}, },
}, },
}; };

View File

@@ -3,7 +3,7 @@ import { Claims } from '../orm/claims';
import invite from '../endpoints/invites'; import invite from '../endpoints/invites';
export default { export default {
POST: guard(invite.create, [Claims.ADMIN, Claims.USERS.INVITE]), POST: guard(invite.create, Claims.ADMIN, Claims.USERS.INVITE),
accept: { accept: {
POST: unwrapMethod(invite.accept), POST: unwrapMethod(invite.accept),
}, },

View File

@@ -3,12 +3,12 @@ import matches from '../endpoints/matches';
import { Claims } from '../orm/claims'; import { Claims } from '../orm/claims';
export default { export default {
'POST': guard(matches.create, [Claims.ADMIN, Claims.MATCHES.CREATE]), 'POST': guard(matches.create, Claims.ADMIN, Claims.MATCHES.CREATE),
':id': { ':id': {
GET: guard(matches.get, [Claims.ADMIN, Claims.MATCHES.OWNED.READ, Claims.MATCHES.PARTICIPANT.READ]), 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]), DELETE: guard(matches.drop, Claims.ADMIN, Claims.MATCHES.OWNED.DELETE, Claims.USERS.SELF.UPDATE),
leave: { leave: {
POST: guard(matches.leave, [Claims.MATCHES.PARTICIPANT.LEAVE]), POST: guard(matches.leave, Claims.MATCHES.PARTICIPANT.LEAVE),
}, },
}, },
}; };

View File

@@ -3,14 +3,14 @@ import { Claims } from '../orm/claims';
import player from '../endpoints/players'; import player from '../endpoints/players';
export default { export default {
'POST': guard(player.create, [Claims.ADMIN, Claims.PLAYERS.CREATE]), 'POST': guard(player.create, Claims.ADMIN, Claims.PLAYERS.CREATE),
':id': { ':id': {
GET: guard(player.get, [Claims.ADMIN, Claims.PLAYERS.OTHER.READ, Claims.PLAYERS.SELF.READ]), 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]), 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]), DELETE: guard(player.drop, Claims.ADMIN, Claims.PLAYERS.OTHER.DELETE, Claims.PLAYERS.SELF.DELETE),
}, },
'list': { 'list': {
variants: [':pageSize/:page', ':page'], variants: [':pageSize/:page', ':page'],
GET: guard(player.list, [Claims.ADMIN, Claims.PLAYERS.OTHER.READ]), GET: guard(player.list, Claims.ADMIN, Claims.PLAYERS.OTHER.READ),
}, },
}; };

View File

@@ -3,10 +3,10 @@ import user from '../endpoints/users';
import { Claims } from '../orm/claims'; import { Claims } from '../orm/claims';
export default { export default {
'POST': guard(user.create, [Claims.ADMIN, Claims.USERS.CREATE]), 'POST': guard(user.create, Claims.ADMIN, Claims.USERS.CREATE),
':id': { ':id': {
GET: guard(user.get, [Claims.ADMIN, Claims.USERS.OTHER.READ, Claims.USERS.SELF.READ]), 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]), 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]), DELETE: guard(user.drop, Claims.ADMIN, Claims.USERS.OTHER.UPDATE, Claims.USERS.SELF.UPDATE),
}, },
}; };

View File

@@ -6,10 +6,10 @@ import { Claims } from '../orm/claims';
export function guardRedirect( export function guardRedirect(
method: (request: UnwrappedRequest<any>) => Promise<Response> | Response, method: (request: UnwrappedRequest<any>) => Promise<Response> | Response,
redirectMethod: Function, redirectMethod: Function,
guardedClaims: string[], ...guardedClaims: string[]
) { ) {
try { try {
return guard(method, guardedClaims); return guard(method, ...guardedClaims);
} catch (e) { } catch (e) {
return redirectMethod(); return redirectMethod();
} }
@@ -17,7 +17,7 @@ export function guardRedirect(
export function guard( export function guard(
method: (request: UnwrappedRequest<any>) => Promise<Response> | Response, method: (request: UnwrappedRequest<any>) => Promise<Response> | Response,
guardedClaims: string[], ...guardedClaims: string[]
): (r: Request) => Promise<Response> { ): (r: Request) => Promise<Response> {
return async (request: Request): Promise<Response> => { return async (request: Request): Promise<Response> => {
const authHeader: string | null = const authHeader: string | null =
@@ -26,10 +26,7 @@ export function guard(
const userClaims: Claims = new Claims( const userClaims: Claims = new Claims(
jwt.verify(authHeader as string, process.env.JWT_SECRET_KEY as string) as any, jwt.verify(authHeader as string, process.env.JWT_SECRET_KEY as string) as any,
); );
if ( if (!userClaims.userId.raw || !userClaims.claims.some((x: string): boolean => guardedClaims.includes(x))) {
!userClaims.userId.raw ||
!userClaims.claims.some((x: string): boolean => guardedClaims.includes(x))
) {
return new UnauthorizedResponse('Unauthorized'); return new UnauthorizedResponse('Unauthorized');
} }
return method(await unwrap(request, userClaims)); return method(await unwrap(request, userClaims));