feat: Add Games (#276)

## Description
<!-- Briefly describe the purpose and scope of your changes -->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced comprehensive management of game libraries, including
adding, removing, and listing games in a user's Steam library.
- Added new API endpoints for retrieving detailed game information by ID
and listing all games in a user's library.
- Enabled friend-related API endpoints to list friends and fetch friend
details by SteamID.
- Added category and base game data structures with validation and
serialization for enriched game metadata.
- Introduced ownership update functionality for Steam accounts during
login.
- Added new game and category linking to support detailed game metadata
and categorization.
- Introduced member retrieval functions for enhanced team and user
management.

- **Improvements**
- Enhanced authentication to enforce team membership checks and provide
member-level access control.
- Improved Steam account ownership handling to ensure accurate user
association.
  - Added indexes to friend relationships for optimized querying.
  - Refined API routing structure with added game and friend routes.
- Improved friend listing queries for efficiency and data completeness.

- **Bug Fixes**
  - Fixed formatting issues in permissions related to Steam accounts.

- **Other**
- Refined event handling for user account refresh based on user ID
instead of email.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Wanjohi
2025-05-10 08:11:00 +03:00
committed by GitHub
parent d933c1e61d
commit 0b995fa540
23 changed files with 1120 additions and 142 deletions

View File

@@ -0,0 +1,101 @@
import { z } from "zod";
import { fn } from "../utils";
import { Game } from "../game";
import { gamesTable } from "../game/game.sql";
import { createSelectSchema } from "drizzle-zod";
import { steamLibraryTable } from "./library.sql";
import { and, eq, inArray, isNull, sql } from "drizzle-orm";
import { baseGamesTable } from "../base-game/base-game.sql";
import { categoriesTable } from "../categories/categories.sql";
import { createTransaction, useTransaction } from "../drizzle/transaction";
import { Actor } from "../actor";
export namespace Library {
export const Info = createSelectSchema(steamLibraryTable)
.omit({ timeCreated: true, timeDeleted: true, timeUpdated: true })
export type Info = z.infer<typeof Info>;
export const add = fn(
Info,
async (input) =>
createTransaction(async (tx) => {
const results =
await tx
.select()
.from(steamLibraryTable)
.where(
and(
eq(steamLibraryTable.gameID, input.gameID),
eq(steamLibraryTable.ownerID, input.ownerID),
isNull(steamLibraryTable.timeDeleted)
)
)
.execute()
if (results.length > 0) return null
await tx
.insert(steamLibraryTable)
.values({
ownerID: input.ownerID,
gameID: input.gameID
})
.onConflictDoUpdate({
target: [steamLibraryTable.ownerID, steamLibraryTable.gameID],
set: { timeDeleted: null }
})
})
)
export const remove = fn(
Info,
(input) =>
useTransaction(async (tx) =>
tx
.update(steamLibraryTable)
.set({ timeDeleted: sql`now()` })
.where(
and(
eq(steamLibraryTable.ownerID, input.ownerID),
eq(steamLibraryTable.gameID, input.gameID),
)
)
)
)
export const list = () =>
useTransaction(async (tx) =>
tx
.select({
games: baseGamesTable,
categories: categoriesTable,
})
.from(steamLibraryTable)
.where(
and(
eq(steamLibraryTable.ownerID, Actor.steamID()),
isNull(steamLibraryTable.timeDeleted)
)
)
.innerJoin(
baseGamesTable,
eq(baseGamesTable.id, steamLibraryTable.gameID),
)
.leftJoin(
gamesTable,
eq(gamesTable.baseGameID, baseGamesTable.id),
)
.leftJoin(
categoriesTable,
and(
eq(categoriesTable.slug, gamesTable.categorySlug),
eq(categoriesTable.type, gamesTable.categoryType),
)
)
.execute()
.then(rows => Game.serialize(rows))
)
}