feat(core): Implement Steam library sync with metadata extraction and image processing (#278)

## 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**
- Added AWS queue infrastructure and SQS handler for processing Steam
game libraries and images.
- Introduced event-driven handling for new credentials and game
additions, including image uploads to S3.
- Added client functions to fetch Steam user libraries, friends lists,
app info, and related images.
- Added new database columns and schema updates to track game
acquisition, playtime, and family sharing.
  - Added utility function for chunking arrays.
- Added new event notifications for library queue processing and game
creation.
  - Added new lookup functions for categories and teams by slug.
- Introduced a new Team API with endpoints to list and fetch teams by
slug.
  - Added a new Steam library page displaying game images.

- **Enhancements**
  - Improved game creation with event notifications and upsert logic.
  - Enhanced category and team retrieval with new lookup functions.
  - Renamed and refined image categories for clearer classification.
  - Expanded dependencies for image processing and AWS SDK integration.
- Improved image processing utilities with caching, ranking, and
metadata extraction.
  - Refined Steam client utilities for concurrency and error handling.

- **Bug Fixes**
- Fixed event publishing timing and removed deprecated credential
retrieval methods.

- **Chores**
- Updated infrastructure configurations with increased timeouts, memory,
and resource linking.
- Added new dependencies for image processing, caching, and AWS SDK
clients.
  - Refined internal code structure and imports for clarity.
  - Removed Steam provider and related UI components from the frontend.
- Disabled authentication providers and Steam-related routes in the
frontend.
  - Updated API fetch handler to accept environment bindings.

- **Refactor**
- Simplified query result handling and renamed functions for better
clarity.
- Removed outdated event handler in favor of consolidated event
subscriber.
- Consolidated and simplified database relationships and permission
queries.

- **Tests**
  - No explicit test changes included in this release.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Wanjohi
2025-05-17 00:51:18 +03:00
committed by GitHub
parent cc2065299d
commit e1a903a7c9
82 changed files with 7819 additions and 1002 deletions

View File

@@ -6,6 +6,7 @@ import {
table,
number,
string,
boolean,
enumeration,
createSchema,
relationships,
@@ -116,6 +117,10 @@ const game_libraries = table("game_libraries")
.columns({
base_game_id: string(),
owner_id: string(),
time_acquired: number(),
last_played: number(),
total_playtime: number(),
is_family_shared: boolean(),
...timestamps
}).primaryKey("base_game_id", "owner_id")
@@ -145,21 +150,30 @@ export const schema = createSchema({
destSchema: members,
destField: ["steam_id"],
}),
friends: r.many({
sourceField: ["id"],
destSchema: friends_list,
destField: ["steam_id"],
}),
friendOf: r.many({
sourceField: ["id"],
destSchema: friends_list,
destField: ["friend_steam_id"],
}),
libraries: r.many({
sourceField: ["id"],
destSchema: game_libraries,
destField: ["owner_id"]
})
friends: r.many(
{
sourceField: ["id"],
destSchema: friends_list,
destField: ["steam_id"],
},
{
sourceField: ["friend_steam_id"],
destSchema: steam_accounts,
destField: ["id"],
}
),
libraryGames: r.many(
{
sourceField: ["id"],
destSchema: game_libraries,
destField: ["owner_id"],
},
{
sourceField: ["base_game_id"],
destSchema: base_games,
destField: ["id"],
}
),
})),
relationships(users, (r) => ({
teams: r.many({
@@ -207,29 +221,31 @@ export const schema = createSchema({
destField: ["id"],
}),
})),
relationships(friends_list, (r) => ({
steam: r.one({
sourceField: ["steam_id"],
destSchema: steam_accounts,
destField: ["id"],
}),
friend: r.one({
sourceField: ["friend_steam_id"],
destSchema: steam_accounts,
destField: ["id"],
}),
})),
relationships(base_games, (r) => ({
games: r.many({
sourceField: ["id"],
destSchema: games,
destField: ["base_game_id"]
}),
libraries: r.many({
sourceField: ["id"],
destSchema: game_libraries,
destField: ["base_game_id"]
}),
libraryOwners: r.many(
{
sourceField: ["id"],
destSchema: game_libraries,
destField: ["base_game_id"],
},
{
sourceField: ["owner_id"],
destSchema: steam_accounts,
destField: ["id"],
}
),
categories: r.many(
{
sourceField: ["id"],
destSchema: games,
destField: ["base_game_id"],
},
{
sourceField: ["category_slug", "type"],
destSchema: categories,
destField: ["slug", "type"],
}
),
images: r.many({
sourceField: ["id"],
destSchema: images,
@@ -237,45 +253,18 @@ export const schema = createSchema({
})
})),
relationships(categories, (r) => ({
games_slug: r.many({
sourceField: ["slug"],
destSchema: games,
destField: ["category_slug"]
}),
games_type: r.many({
sourceField: ["type"],
destSchema: games,
destField: ["type"]
})
})),
relationships(games, (r) => ({
category_slug: r.one({
sourceField: ["category_slug"],
destSchema: categories,
destField: ["slug"],
}),
category_type: r.one({
sourceField: ["type"],
destSchema: categories,
destField: ["type"],
}),
base_game: r.one({
sourceField: ["base_game_id"],
destSchema: base_games,
destField: ["id"],
}),
})),
relationships(game_libraries, (r) => ({
base_game: r.one({
sourceField: ["base_game_id"],
destSchema: base_games,
destField: ["id"],
}),
owner: r.one({
sourceField: ["owner_id"],
destSchema: steam_accounts,
destField: ["id"],
}),
baseGames: r.many(
{
sourceField: ["slug", "type"],
destSchema: games,
destField: ["category_slug", "type"],
},
{
sourceField: ["base_game_id"],
destSchema: base_games,
destField: ["id"],
}
),
})),
relationships(images, (r) => ({
base_game: r.one({
@@ -284,7 +273,45 @@ export const schema = createSchema({
destField: ["id"],
}),
})),
//Junction tables
relationships(friends_list, (r) => ({
steam: r.one({
sourceField: ["steam_id"],
destSchema: steam_accounts,
destField: ["id"]
}),
friend: r.one({
sourceField: ["friend_steam_id"],
destSchema: steam_accounts,
destField: ["id"]
}),
})),
relationships(game_libraries, (r) => ({
owner: r.one({
sourceField: ["owner_id"],
destSchema: steam_accounts,
destField: ["id"]
}),
baseGame: r.one({
sourceField: ["base_game_id"],
destSchema: base_games,
destField: ["id"]
}),
})),
relationships(games, (r) => ({
baseGame: r.one({
sourceField: ["base_game_id"],
destSchema: base_games,
destField: ["id"]
}),
category: r.one({
sourceField: ["category_slug", "type"],
destSchema: categories,
destField: ["slug", "type"]
}),
})),
],
});
@@ -321,7 +348,7 @@ export const permissions = definePermissions<Auth, Schema>(schema, () => {
select: [
(auth: Auth, q: ExpressionBuilder<Schema, 'steam_accounts'>) => q.exists("user", (u) => u.where("id", auth.sub)),
//Allow friends to view friends steam accounts
(auth: Auth, q: ExpressionBuilder<Schema, 'steam_accounts'>) => q.exists("friends", (u) => u.related("friend", (f) => f.where("user_id", auth.sub))),
(auth: Auth, q: ExpressionBuilder<Schema, 'steam_accounts'>) => q.exists("friends", (u) => u.where("user_id", auth.sub)),
//allow other team members to see a user's steam account
(auth: Auth, q: ExpressionBuilder<Schema, 'steam_accounts'>) => q.exists("memberEntries", (u) => u.related("team", (t) => t.related("members", (m) => m.where("user_id", auth.sub)))),
]
@@ -349,7 +376,7 @@ export const permissions = definePermissions<Auth, Schema>(schema, () => {
//allow team members to see the other members' libraries
(auth: Auth, q: ExpressionBuilder<Schema, 'game_libraries'>) => q.exists("owner", (u) => u.related("memberEntries", (f) => f.where("user_id", auth.sub))),
//allow friends to see their friends libraries
(auth: Auth, q: ExpressionBuilder<Schema, 'game_libraries'>) => q.exists("owner", (u) => u.related("friends", (f) => f.related("friend", (s) => s.where("user_id", auth.sub)))),
(auth: Auth, q: ExpressionBuilder<Schema, 'game_libraries'>) => q.exists("owner", (u) => u.related("friends", (f) => f.where("user_id", auth.sub))),
]
}
},