Implemented the ability to add/remove games from collections.

This commit is contained in:
jd
2026-02-21 17:27:46 +00:00
parent 59d2819750
commit afa1c13e13
5 changed files with 98 additions and 2 deletions

View File

@@ -1,8 +1,12 @@
import { orm } from '../orm/orm';
import { UnwrappedRequest } from '../utilities/guard';
import { CreatedResponse, ErrorResponse, OkResponse } from '../utilities/responseHelper';
import { CreateCollectionRequest, UpdateCollectionRequest } from '../utilities/requestModels';
import { CollectionId } from '../utilities/secureIds';
import {
GameToCollectionRequest,
CreateCollectionRequest,
UpdateCollectionRequest,
} from '../utilities/requestModels';
import { CollectionId, GameId } from '../utilities/secureIds';
async function create(request: UnwrappedRequest<CreateCollectionRequest>): Promise<Response> {
try {
@@ -47,10 +51,40 @@ async function drop(request: UnwrappedRequest): Promise<Response> {
}
}
async function addGame(request: UnwrappedRequest<GameToCollectionRequest>): Promise<Response> {
try {
return new OkResponse(
await orm.collections.addGame(
CollectionId.fromHash(request.params.id),
GameId.fromHash(request.body.gameId),
request.claims,
),
);
} catch (error: any) {
return new ErrorResponse(error as Error);
}
}
async function removeGame(request: UnwrappedRequest<GameToCollectionRequest>): Promise<Response> {
try {
return new OkResponse(
await orm.collections.removeGame(
CollectionId.fromHash(request.params.id),
GameId.fromHash(request.body.gameId),
request.claims,
),
);
} catch (error: any) {
return new ErrorResponse(error as Error);
}
}
export default {
create,
get,
list,
update,
drop,
addGame,
removeGame,
};

View File

@@ -127,4 +127,41 @@ export class CollectionsOrm {
return;
}
async addGame(id: CollectionId, gameId: GameId, claims: Claims): Promise<void> {
const collection = await this.get(id);
if (!(Claims.test(Claims.ADMIN, claims) || Claims.test(Claims.COLLECTIONS.UNOWNED.GAME.ADD, claims))) {
throw new UnauthorizedError();
} else if (
Claims.test(Claims.COLLECTIONS.OWNED.GAME.ADD, claims) &&
claims?.userId &&
collection.id !== claims.userId
) {
throw new UnauthorizedError();
}
await sql`INSERT INTO collection_games (collection_id, game_id)
VALUES (${id.raw}, ${gameId.raw})`;
return;
}
async removeGame(id: CollectionId, gameId: GameId, claims: Claims): Promise<void> {
const collection = await this.get(id);
if (!(Claims.test(Claims.ADMIN, claims) || Claims.test(Claims.COLLECTIONS.UNOWNED.GAME.REMOVE, claims))) {
throw new UnauthorizedError();
} else if (
Claims.test(Claims.COLLECTIONS.OWNED.GAME.REMOVE, claims) &&
claims?.userId &&
collection.id !== claims.userId
) {
throw new UnauthorizedError();
}
await sql`DELETE
FROM collection_games
WHERE collection_id = ${id.raw}
AND game_id = ${gameId.raw}`;
return;
}
}

View File

@@ -11,6 +11,20 @@ export default {
// 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]),
},
'/api/collection/:id/add': {
POST: guard(collections.addGame, [
Claims.ADMIN,
Claims.COLLECTIONS.UNOWNED.GAME.ADD,
Claims.COLLECTIONS.OWNED.GAME.ADD,
]),
},
'/api/collection/:id/remove': {
POST: guard(collections.removeGame, [
Claims.ADMIN,
Claims.COLLECTIONS.UNOWNED.GAME.REMOVE,
Claims.COLLECTIONS.OWNED.GAME.REMOVE,
]),
},
'/api/collection/list': {
GET: guard(collections.list, [Claims.ADMIN, Claims.COLLECTIONS.OWNED.LIST]),
},

View File

@@ -108,6 +108,10 @@ export class ClaimDefinition {
UPDATE: 'COLLECTIONS_OWNED_UPDATE',
DELETE: 'COLLECTIONS_OWNED_DELETE',
LIST: 'COLLECTIONS_OWNED_LIST',
GAME: {
ADD: 'COLLECTIONS_OWNED_GAME_ADD',
REMOVE: 'COLLECTIONS_OWNED_GAME_REMOVE',
},
COMMENTS: {
DELETE: 'COLLECTIONS_OWNED_COMMENTS_DELETE',
},
@@ -116,6 +120,10 @@ export class ClaimDefinition {
READ: 'COLLECTIONS_UNOWNED_READ',
UPDATE: 'COLLECTIONS_UNOWNED_UPDATE',
DELETE: 'COLLECTIONS_UNOWNED_DELETE',
GAME: {
ADD: 'COLLECTIONS_UNOWNED_GAME_ADD',
REMOVE: 'COLLECTIONS_UNOWNED_GAME_REMOVE',
},
},
};
public static readonly COMMENTS = {

View File

@@ -47,3 +47,6 @@ export interface CreateCollectionRequest {
export interface UpdateCollectionRequest {
name?: string;
}
export interface GameToCollectionRequest {
gameId: string;
}