@playcademy/sandbox 0.1.0-beta.7 → 0.1.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +630 -285
- package/dist/server.js +631 -286
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -52853,7 +52853,8 @@ var PLAYCADEMY_CREDITS_ID = crypto.randomUUID();
|
|
|
52853
52853
|
var SAMPLE_ITEMS = [
|
|
52854
52854
|
{
|
|
52855
52855
|
id: PLAYCADEMY_CREDITS_ID,
|
|
52856
|
-
|
|
52856
|
+
slug: "PLAYCADEMY_CREDITS",
|
|
52857
|
+
gameId: null,
|
|
52857
52858
|
displayName: "PLAYCADEMY credits",
|
|
52858
52859
|
description: "The main currency used across PLAYCADEMY.",
|
|
52859
52860
|
type: "currency",
|
|
@@ -52864,7 +52865,8 @@ var SAMPLE_ITEMS = [
|
|
|
52864
52865
|
},
|
|
52865
52866
|
{
|
|
52866
52867
|
id: crypto.randomUUID(),
|
|
52867
|
-
|
|
52868
|
+
slug: "FOUNDING_MEMBER_BADGE",
|
|
52869
|
+
gameId: null,
|
|
52868
52870
|
displayName: "Founding Member Badge",
|
|
52869
52871
|
description: "Reserved for founding core team of the PLAYCADEMY platform.",
|
|
52870
52872
|
type: "badge",
|
|
@@ -52875,7 +52877,8 @@ var SAMPLE_ITEMS = [
|
|
|
52875
52877
|
},
|
|
52876
52878
|
{
|
|
52877
52879
|
id: crypto.randomUUID(),
|
|
52878
|
-
|
|
52880
|
+
slug: "EARLY_ADOPTER_BADGE",
|
|
52881
|
+
gameId: null,
|
|
52879
52882
|
displayName: "Early Adopter Badge",
|
|
52880
52883
|
description: "Awarded to users who joined during the beta phase.",
|
|
52881
52884
|
type: "badge",
|
|
@@ -52886,7 +52889,8 @@ var SAMPLE_ITEMS = [
|
|
|
52886
52889
|
},
|
|
52887
52890
|
{
|
|
52888
52891
|
id: crypto.randomUUID(),
|
|
52889
|
-
|
|
52892
|
+
slug: "FIRST_GAME_BADGE",
|
|
52893
|
+
gameId: null,
|
|
52890
52894
|
displayName: "First Game Played",
|
|
52891
52895
|
description: "Awarded for playing your first game in the Playcademy platform.",
|
|
52892
52896
|
type: "badge",
|
|
@@ -52897,7 +52901,8 @@ var SAMPLE_ITEMS = [
|
|
|
52897
52901
|
},
|
|
52898
52902
|
{
|
|
52899
52903
|
id: crypto.randomUUID(),
|
|
52900
|
-
|
|
52904
|
+
slug: "COMMON_SWORD",
|
|
52905
|
+
gameId: null,
|
|
52901
52906
|
displayName: "Common Sword",
|
|
52902
52907
|
description: "A basic sword, good for beginners.",
|
|
52903
52908
|
type: "unlock",
|
|
@@ -52906,7 +52911,8 @@ var SAMPLE_ITEMS = [
|
|
|
52906
52911
|
},
|
|
52907
52912
|
{
|
|
52908
52913
|
id: crypto.randomUUID(),
|
|
52909
|
-
|
|
52914
|
+
slug: "SMALL_HEALTH_POTION",
|
|
52915
|
+
gameId: null,
|
|
52910
52916
|
displayName: "Small Health Potion",
|
|
52911
52917
|
description: "Restores a small amount of health.",
|
|
52912
52918
|
type: "other",
|
|
@@ -52915,7 +52921,8 @@ var SAMPLE_ITEMS = [
|
|
|
52915
52921
|
},
|
|
52916
52922
|
{
|
|
52917
52923
|
id: crypto.randomUUID(),
|
|
52918
|
-
|
|
52924
|
+
slug: "SMALL_BACKPACK",
|
|
52925
|
+
gameId: null,
|
|
52919
52926
|
displayName: "Small Backpack",
|
|
52920
52927
|
description: "Increases your inventory capacity by 5 slots.",
|
|
52921
52928
|
type: "upgrade",
|
|
@@ -77388,14 +77395,20 @@ var itemTypeEnum = pgEnum("item_type", [
|
|
|
77388
77395
|
]);
|
|
77389
77396
|
var items = pgTable("items", {
|
|
77390
77397
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
77391
|
-
|
|
77398
|
+
slug: text("slug").notNull(),
|
|
77399
|
+
gameId: uuid("game_id").references(() => games.id, {
|
|
77400
|
+
onDelete: "cascade"
|
|
77401
|
+
}),
|
|
77392
77402
|
displayName: text("display_name").notNull(),
|
|
77393
77403
|
description: text("description"),
|
|
77394
77404
|
type: itemTypeEnum("type").notNull().default("other"),
|
|
77395
77405
|
imageUrl: text("image_url"),
|
|
77396
77406
|
metadata: jsonb("metadata").default({}),
|
|
77397
77407
|
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
77398
|
-
})
|
|
77408
|
+
}, (table) => [
|
|
77409
|
+
uniqueIndex("items_game_slug_idx").on(table.gameId, table.slug),
|
|
77410
|
+
uniqueIndex("items_global_slug_idx").on(table.slug).where(sql`game_id IS NULL`)
|
|
77411
|
+
]);
|
|
77399
77412
|
var inventoryItems = pgTable("inventory_items", {
|
|
77400
77413
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
77401
77414
|
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
@@ -77428,9 +77441,13 @@ var shopListings = pgTable("shop_listings", {
|
|
|
77428
77441
|
}, (table) => [
|
|
77429
77442
|
uniqueIndex("unique_item_currency_listing_idx").on(table.itemId, table.currencyId)
|
|
77430
77443
|
]);
|
|
77431
|
-
var itemsRelations = relations(items, ({ many }) => ({
|
|
77444
|
+
var itemsRelations = relations(items, ({ many, one }) => ({
|
|
77432
77445
|
shopListings: many(shopListings),
|
|
77433
|
-
inventoryItems: many(inventoryItems)
|
|
77446
|
+
inventoryItems: many(inventoryItems),
|
|
77447
|
+
game: one(games, {
|
|
77448
|
+
fields: [items.gameId],
|
|
77449
|
+
references: [games.id]
|
|
77450
|
+
})
|
|
77434
77451
|
}));
|
|
77435
77452
|
var currenciesRelations = relations(currencies, ({ many }) => ({
|
|
77436
77453
|
shopListings: many(shopListings)
|
|
@@ -77479,14 +77496,18 @@ var ItemMetadataSchema = exports_external.object({
|
|
|
77479
77496
|
var InsertItemSchema = createInsertSchema(items, {
|
|
77480
77497
|
imageUrl: exports_external.string().refine((val) => validateRelativePath2(val, "imageUrl")).optional().nullable(),
|
|
77481
77498
|
type: exports_external.enum(itemTypeEnum.enumValues).default("other"),
|
|
77482
|
-
metadata: ItemMetadataSchema.optional()
|
|
77499
|
+
metadata: ItemMetadataSchema.optional(),
|
|
77500
|
+
gameId: exports_external.string().uuid().optional().nullable()
|
|
77483
77501
|
}).omit({ id: true });
|
|
77484
77502
|
var SelectItemSchema = createSelectSchema(items);
|
|
77485
77503
|
var UpdateItemSchema = InsertItemSchema.pick({
|
|
77504
|
+
slug: true,
|
|
77505
|
+
displayName: true,
|
|
77506
|
+
description: true,
|
|
77486
77507
|
type: true,
|
|
77487
77508
|
metadata: true,
|
|
77488
77509
|
imageUrl: true
|
|
77489
|
-
});
|
|
77510
|
+
}).partial();
|
|
77490
77511
|
var InsertInventoryItemSchema = createInsertSchema(inventoryItems).omit({
|
|
77491
77512
|
id: true,
|
|
77492
77513
|
updatedAt: true
|
|
@@ -77722,7 +77743,7 @@ async function seedCurrentProjectGame(db, project) {
|
|
|
77722
77743
|
// package.json
|
|
77723
77744
|
var package_default = {
|
|
77724
77745
|
name: "@playcademy/sandbox",
|
|
77725
|
-
version: "0.1.0-beta.
|
|
77746
|
+
version: "0.1.0-beta.7",
|
|
77726
77747
|
description: "Local development server for Playcademy game development",
|
|
77727
77748
|
type: "module",
|
|
77728
77749
|
exports: {
|
|
@@ -77827,7 +77848,7 @@ async function getUserMe(ctx) {
|
|
|
77827
77848
|
}
|
|
77828
77849
|
|
|
77829
77850
|
// ../data/src/constants.ts
|
|
77830
|
-
var
|
|
77851
|
+
var ITEM_SLUGS = {
|
|
77831
77852
|
PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
|
|
77832
77853
|
PLAYCADEMY_XP: "PLAYCADEMY_XP",
|
|
77833
77854
|
FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
|
|
@@ -77838,14 +77859,15 @@ var ITEM_INTERNAL_NAMES = {
|
|
|
77838
77859
|
SMALL_BACKPACK: "SMALL_BACKPACK"
|
|
77839
77860
|
};
|
|
77840
77861
|
var CURRENCIES = {
|
|
77841
|
-
PRIMARY:
|
|
77842
|
-
XP:
|
|
77862
|
+
PRIMARY: ITEM_SLUGS.PLAYCADEMY_CREDITS,
|
|
77863
|
+
XP: ITEM_SLUGS.PLAYCADEMY_XP
|
|
77843
77864
|
};
|
|
77844
77865
|
var BADGES = {
|
|
77845
|
-
FOUNDING_MEMBER:
|
|
77846
|
-
EARLY_ADOPTER:
|
|
77847
|
-
FIRST_GAME:
|
|
77866
|
+
FOUNDING_MEMBER: ITEM_SLUGS.FOUNDING_MEMBER_BADGE,
|
|
77867
|
+
EARLY_ADOPTER: ITEM_SLUGS.EARLY_ADOPTER_BADGE,
|
|
77868
|
+
FIRST_GAME: ITEM_SLUGS.FIRST_GAME_BADGE
|
|
77848
77869
|
};
|
|
77870
|
+
var INTERACTION_TYPE = Object.fromEntries(interactionTypeEnum.enumValues.map((value) => [value, value]));
|
|
77849
77871
|
|
|
77850
77872
|
// ../api-core/src/utils/levels.ts
|
|
77851
77873
|
var levelConfigCache = null;
|
|
@@ -78002,7 +78024,7 @@ async function addXP(ctx, amount) {
|
|
|
78002
78024
|
creditsAwarded: creditsToAward
|
|
78003
78025
|
});
|
|
78004
78026
|
if (creditsToAward > 0) {
|
|
78005
|
-
const [creditsItem] = await tx.select({ id: items.id }).from(items).where(eq(items.
|
|
78027
|
+
const [creditsItem] = await tx.select({ id: items.id }).from(items).where(eq(items.slug, CURRENCIES.PRIMARY)).limit(1);
|
|
78006
78028
|
if (!creditsItem) {
|
|
78007
78029
|
throw ApiError.internal(`${CURRENCIES.PRIMARY} item not found`);
|
|
78008
78030
|
}
|
|
@@ -78202,15 +78224,18 @@ async function getUserInventory(ctx) {
|
|
|
78202
78224
|
const db = getDatabase();
|
|
78203
78225
|
const inventory2 = await db.select({
|
|
78204
78226
|
id: inventoryItems.id,
|
|
78227
|
+
userId: inventoryItems.userId,
|
|
78205
78228
|
quantity: inventoryItems.quantity,
|
|
78206
78229
|
item: {
|
|
78207
78230
|
id: items.id,
|
|
78208
|
-
|
|
78231
|
+
slug: items.slug,
|
|
78232
|
+
gameId: items.gameId,
|
|
78209
78233
|
displayName: items.displayName,
|
|
78210
78234
|
description: items.description,
|
|
78211
78235
|
type: items.type,
|
|
78212
78236
|
imageUrl: items.imageUrl,
|
|
78213
|
-
metadata: items.metadata
|
|
78237
|
+
metadata: items.metadata,
|
|
78238
|
+
createdAt: items.createdAt
|
|
78214
78239
|
},
|
|
78215
78240
|
updatedAt: inventoryItems.updatedAt
|
|
78216
78241
|
}).from(inventoryItems).where(eq(inventoryItems.userId, user.id)).innerJoin(items, eq(inventoryItems.itemId, items.id));
|
|
@@ -79839,14 +79864,13 @@ async function listGames(ctx) {
|
|
|
79839
79864
|
if (!user) {
|
|
79840
79865
|
throw ApiError.unauthorized("Must be logged in to list games");
|
|
79841
79866
|
}
|
|
79842
|
-
if (user.role !== "admin" && user.role !== "developer") {
|
|
79843
|
-
throw ApiError.forbidden("Requires admin or developer privileges to list games");
|
|
79844
|
-
}
|
|
79845
79867
|
try {
|
|
79846
79868
|
const db = getDatabase();
|
|
79847
79869
|
let filter2 = undefined;
|
|
79848
79870
|
if (user.role === "developer") {
|
|
79849
79871
|
filter2 = eq(games.developerId, user.id);
|
|
79872
|
+
} else if (user.role === "player") {
|
|
79873
|
+
return [];
|
|
79850
79874
|
}
|
|
79851
79875
|
const games2 = await db.query.games.findMany({
|
|
79852
79876
|
where: filter2,
|
|
@@ -80087,144 +80111,513 @@ async function upsertGameBySlug(ctx) {
|
|
|
80087
80111
|
throw ApiError.internal("Internal server error", error2);
|
|
80088
80112
|
}
|
|
80089
80113
|
}
|
|
80090
|
-
|
|
80091
|
-
|
|
80092
|
-
|
|
80093
|
-
|
|
80094
|
-
|
|
80095
|
-
}
|
|
80096
|
-
return data2[offset];
|
|
80097
|
-
}
|
|
80098
|
-
uint16(data2, offset) {
|
|
80099
|
-
if (data2.byteLength < offset + 2) {
|
|
80100
|
-
throw new TypeError("Insufficient bytes");
|
|
80101
|
-
}
|
|
80102
|
-
return data2[offset] << 8 | data2[offset + 1];
|
|
80114
|
+
|
|
80115
|
+
// ../api-core/src/items/index.ts
|
|
80116
|
+
async function validateGameOwnership(user, gameId) {
|
|
80117
|
+
if (user.role === "admin") {
|
|
80118
|
+
return;
|
|
80103
80119
|
}
|
|
80104
|
-
|
|
80105
|
-
|
|
80106
|
-
|
|
80120
|
+
try {
|
|
80121
|
+
const db = getDatabase();
|
|
80122
|
+
const gameOwnership = await db.query.games.findFirst({
|
|
80123
|
+
where: and(eq(games.id, gameId), eq(games.developerId, user.id)),
|
|
80124
|
+
columns: { id: true }
|
|
80125
|
+
});
|
|
80126
|
+
if (!gameOwnership) {
|
|
80127
|
+
const gameExists = await db.query.games.findFirst({
|
|
80128
|
+
where: eq(games.id, gameId),
|
|
80129
|
+
columns: { id: true }
|
|
80130
|
+
});
|
|
80131
|
+
if (!gameExists) {
|
|
80132
|
+
throw ApiError.notFound("Game not found");
|
|
80133
|
+
}
|
|
80134
|
+
throw ApiError.forbidden("You do not own this game");
|
|
80107
80135
|
}
|
|
80108
|
-
|
|
80109
|
-
|
|
80110
|
-
|
|
80136
|
+
} catch (error2) {
|
|
80137
|
+
if (error2 instanceof ApiError) {
|
|
80138
|
+
throw error2;
|
|
80111
80139
|
}
|
|
80112
|
-
|
|
80140
|
+
logger2.error(`Error checking game ownership for ${gameId}:`, error2);
|
|
80141
|
+
throw ApiError.internal("Internal server error", error2);
|
|
80113
80142
|
}
|
|
80114
|
-
|
|
80115
|
-
|
|
80116
|
-
|
|
80117
|
-
|
|
80118
|
-
|
|
80119
|
-
for (let i3 = 0;i3 < 8; i3++) {
|
|
80120
|
-
result |= BigInt(data2[offset + i3]) << BigInt(56 - i3 * 8);
|
|
80121
|
-
}
|
|
80122
|
-
return result;
|
|
80143
|
+
}
|
|
80144
|
+
async function listItems(ctx) {
|
|
80145
|
+
const user = ctx.user;
|
|
80146
|
+
if (!user) {
|
|
80147
|
+
throw ApiError.unauthorized("Must be logged in to view items");
|
|
80123
80148
|
}
|
|
80124
|
-
|
|
80125
|
-
|
|
80126
|
-
|
|
80149
|
+
try {
|
|
80150
|
+
const db = getDatabase();
|
|
80151
|
+
const url2 = new URL(ctx.request.url);
|
|
80152
|
+
const gameId = url2.searchParams.get("gameId");
|
|
80153
|
+
let allItems;
|
|
80154
|
+
if (gameId) {
|
|
80155
|
+
allItems = await db.query.items.findMany({
|
|
80156
|
+
where: eq(items.gameId, gameId)
|
|
80157
|
+
});
|
|
80158
|
+
} else {
|
|
80159
|
+
allItems = await db.query.items.findMany();
|
|
80127
80160
|
}
|
|
80128
|
-
|
|
80129
|
-
|
|
80161
|
+
return allItems;
|
|
80162
|
+
} catch (error2) {
|
|
80163
|
+
if (error2 instanceof ApiError) {
|
|
80164
|
+
throw error2;
|
|
80130
80165
|
}
|
|
80131
|
-
|
|
80166
|
+
logger2.error("Error fetching items:", error2);
|
|
80167
|
+
throw ApiError.internal("Internal server error", error2);
|
|
80132
80168
|
}
|
|
80133
|
-
|
|
80134
|
-
|
|
80135
|
-
|
|
80169
|
+
}
|
|
80170
|
+
async function getItemById(ctx) {
|
|
80171
|
+
const user = ctx.user;
|
|
80172
|
+
const itemId = ctx.params.itemId;
|
|
80173
|
+
if (!user) {
|
|
80174
|
+
throw ApiError.unauthorized("Must be logged in to view item details");
|
|
80175
|
+
}
|
|
80176
|
+
if (!itemId) {
|
|
80177
|
+
throw ApiError.badRequest("Missing item ID");
|
|
80178
|
+
}
|
|
80179
|
+
try {
|
|
80180
|
+
const db = getDatabase();
|
|
80181
|
+
const item = await db.query.items.findFirst({
|
|
80182
|
+
where: eq(items.id, itemId)
|
|
80183
|
+
});
|
|
80184
|
+
if (!item) {
|
|
80185
|
+
throw ApiError.notFound("Item not found");
|
|
80136
80186
|
}
|
|
80137
|
-
|
|
80138
|
-
|
|
80187
|
+
return item;
|
|
80188
|
+
} catch (error2) {
|
|
80189
|
+
if (error2 instanceof ApiError) {
|
|
80190
|
+
throw error2;
|
|
80139
80191
|
}
|
|
80140
|
-
|
|
80141
|
-
|
|
80192
|
+
logger2.error(`Error fetching item ${itemId}:`, error2);
|
|
80193
|
+
throw ApiError.internal("Internal server error", error2);
|
|
80142
80194
|
}
|
|
80143
|
-
|
|
80144
|
-
|
|
80145
|
-
|
|
80146
|
-
|
|
80147
|
-
|
|
80148
|
-
|
|
80195
|
+
}
|
|
80196
|
+
async function createItem(ctx) {
|
|
80197
|
+
const user = ctx.user;
|
|
80198
|
+
if (!user || user.role !== "admin") {
|
|
80199
|
+
throw ApiError.forbidden("Admin access required");
|
|
80200
|
+
}
|
|
80201
|
+
let inputData;
|
|
80202
|
+
try {
|
|
80203
|
+
const requestBody = await ctx.request.json();
|
|
80204
|
+
const validationResult = InsertItemSchema.safeParse(requestBody);
|
|
80205
|
+
if (!validationResult.success) {
|
|
80206
|
+
throw ApiError.badRequest(`Validation failed: ${formatValidationErrors(validationResult.error)}`);
|
|
80149
80207
|
}
|
|
80150
|
-
|
|
80151
|
-
|
|
80208
|
+
inputData = validationResult.data;
|
|
80209
|
+
} catch (error2) {
|
|
80210
|
+
if (error2 instanceof ApiError) {
|
|
80211
|
+
throw error2;
|
|
80152
80212
|
}
|
|
80213
|
+
logger2.error("Failed to parse request body or invalid JSON:", error2);
|
|
80214
|
+
throw ApiError.badRequest("Invalid JSON body");
|
|
80153
80215
|
}
|
|
80154
|
-
|
|
80155
|
-
|
|
80156
|
-
|
|
80157
|
-
|
|
80158
|
-
|
|
80159
|
-
throw new TypeError("Invalid uint64 value");
|
|
80216
|
+
try {
|
|
80217
|
+
const db = getDatabase();
|
|
80218
|
+
const [newItem] = await db.insert(items).values(inputData).returning();
|
|
80219
|
+
if (!newItem) {
|
|
80220
|
+
throw ApiError.internal("Failed to create item in database");
|
|
80160
80221
|
}
|
|
80161
|
-
|
|
80162
|
-
|
|
80222
|
+
return newItem;
|
|
80223
|
+
} catch (error2) {
|
|
80224
|
+
if (error2 instanceof Error) {
|
|
80225
|
+
if (error2.message.includes("duplicate key value violates unique constraint")) {
|
|
80226
|
+
const gameScope = inputData.gameId ? "for this game" : "as a platform item";
|
|
80227
|
+
throw ApiError.conflict(`An item with slug '${inputData.slug}' already exists ${gameScope}`);
|
|
80228
|
+
}
|
|
80163
80229
|
}
|
|
80230
|
+
logger2.error("Error creating item:", error2);
|
|
80231
|
+
throw ApiError.internal("Internal server error", error2);
|
|
80164
80232
|
}
|
|
80165
80233
|
}
|
|
80166
|
-
|
|
80167
|
-
|
|
80168
|
-
|
|
80169
|
-
|
|
80170
|
-
|
|
80171
|
-
}
|
|
80172
|
-
return data2[offset];
|
|
80234
|
+
async function updateItem(ctx) {
|
|
80235
|
+
const user = ctx.user;
|
|
80236
|
+
const itemId = ctx.params.itemId;
|
|
80237
|
+
if (!user || user.role !== "admin") {
|
|
80238
|
+
throw ApiError.forbidden("Admin access required");
|
|
80173
80239
|
}
|
|
80174
|
-
|
|
80175
|
-
|
|
80176
|
-
throw new TypeError("Insufficient bytes");
|
|
80177
|
-
}
|
|
80178
|
-
return data2[offset] | data2[offset + 1] << 8;
|
|
80240
|
+
if (!itemId) {
|
|
80241
|
+
throw ApiError.badRequest("Missing item ID");
|
|
80179
80242
|
}
|
|
80180
|
-
|
|
80181
|
-
|
|
80182
|
-
|
|
80243
|
+
let inputData;
|
|
80244
|
+
try {
|
|
80245
|
+
const requestBody = await ctx.request.json();
|
|
80246
|
+
const validationResult = UpdateItemSchema.safeParse(requestBody);
|
|
80247
|
+
if (!validationResult.success) {
|
|
80248
|
+
throw ApiError.badRequest(`Validation failed: ${formatValidationErrors(validationResult.error)}`);
|
|
80183
80249
|
}
|
|
80184
|
-
|
|
80185
|
-
|
|
80186
|
-
|
|
80250
|
+
inputData = validationResult.data;
|
|
80251
|
+
} catch (error2) {
|
|
80252
|
+
if (error2 instanceof ApiError) {
|
|
80253
|
+
throw error2;
|
|
80187
80254
|
}
|
|
80188
|
-
|
|
80255
|
+
logger2.error("Failed to parse request body or invalid JSON:", error2);
|
|
80256
|
+
throw ApiError.badRequest("Invalid JSON body");
|
|
80189
80257
|
}
|
|
80190
|
-
|
|
80191
|
-
|
|
80192
|
-
throw new TypeError("Insufficient bytes");
|
|
80193
|
-
}
|
|
80194
|
-
let result = 0n;
|
|
80195
|
-
for (let i3 = 0;i3 < 8; i3++) {
|
|
80196
|
-
result |= BigInt(data2[offset + i3]) << BigInt(i3 * 8);
|
|
80197
|
-
}
|
|
80198
|
-
return result;
|
|
80258
|
+
if (Object.keys(inputData).length === 0) {
|
|
80259
|
+
throw ApiError.badRequest("No update data provided");
|
|
80199
80260
|
}
|
|
80200
|
-
|
|
80201
|
-
|
|
80202
|
-
|
|
80261
|
+
try {
|
|
80262
|
+
const db = getDatabase();
|
|
80263
|
+
const [updatedItem] = await db.update(items).set(inputData).where(eq(items.id, itemId)).returning();
|
|
80264
|
+
if (!updatedItem) {
|
|
80265
|
+
throw ApiError.notFound("Item not found for update");
|
|
80203
80266
|
}
|
|
80204
|
-
|
|
80205
|
-
|
|
80267
|
+
return updatedItem;
|
|
80268
|
+
} catch (error2) {
|
|
80269
|
+
if (error2 instanceof Error) {
|
|
80270
|
+
if (error2.message.includes("duplicate key value violates unique constraint")) {
|
|
80271
|
+
throw ApiError.conflict("An item with this slug already exists within the same scope (platform or game)");
|
|
80272
|
+
}
|
|
80206
80273
|
}
|
|
80207
|
-
|
|
80274
|
+
logger2.error(`Error updating item ${itemId}:`, error2);
|
|
80275
|
+
throw ApiError.internal("Internal server error", error2);
|
|
80208
80276
|
}
|
|
80209
|
-
|
|
80210
|
-
|
|
80211
|
-
|
|
80212
|
-
|
|
80213
|
-
|
|
80214
|
-
|
|
80215
|
-
}
|
|
80216
|
-
target[offset + 1] = value >> 8;
|
|
80217
|
-
target[offset] = value & 255;
|
|
80277
|
+
}
|
|
80278
|
+
async function deleteItem(ctx) {
|
|
80279
|
+
const user = ctx.user;
|
|
80280
|
+
const itemId = ctx.params.itemId;
|
|
80281
|
+
if (!user || user.role !== "admin") {
|
|
80282
|
+
throw ApiError.forbidden("Admin access required");
|
|
80218
80283
|
}
|
|
80219
|
-
|
|
80220
|
-
|
|
80221
|
-
|
|
80222
|
-
|
|
80223
|
-
|
|
80224
|
-
|
|
80284
|
+
if (!itemId) {
|
|
80285
|
+
throw ApiError.badRequest("Missing item ID");
|
|
80286
|
+
}
|
|
80287
|
+
try {
|
|
80288
|
+
const db = getDatabase();
|
|
80289
|
+
const result = await db.delete(items).where(eq(items.id, itemId)).returning({ id: items.id });
|
|
80290
|
+
if (result.length === 0) {
|
|
80291
|
+
throw ApiError.notFound("Item not found for deletion");
|
|
80225
80292
|
}
|
|
80226
|
-
|
|
80227
|
-
|
|
80293
|
+
} catch (error2) {
|
|
80294
|
+
if (error2 instanceof ApiError) {
|
|
80295
|
+
throw error2;
|
|
80296
|
+
}
|
|
80297
|
+
logger2.error(`Error deleting item ${itemId}:`, error2);
|
|
80298
|
+
throw ApiError.internal("Internal server error", error2);
|
|
80299
|
+
}
|
|
80300
|
+
}
|
|
80301
|
+
async function resolveItem(ctx) {
|
|
80302
|
+
const user = ctx.user;
|
|
80303
|
+
const url2 = new URL(ctx.request.url);
|
|
80304
|
+
const slug = url2.searchParams.get("slug");
|
|
80305
|
+
const gameId = url2.searchParams.get("gameId");
|
|
80306
|
+
if (!user) {
|
|
80307
|
+
throw ApiError.unauthorized("Must be logged in to resolve items");
|
|
80308
|
+
}
|
|
80309
|
+
if (!slug) {
|
|
80310
|
+
throw ApiError.badRequest("Missing slug parameter");
|
|
80311
|
+
}
|
|
80312
|
+
try {
|
|
80313
|
+
const db = getDatabase();
|
|
80314
|
+
if (gameId) {
|
|
80315
|
+
const gameItem = await db.query.items.findFirst({
|
|
80316
|
+
where: and(eq(items.slug, slug), eq(items.gameId, gameId))
|
|
80317
|
+
});
|
|
80318
|
+
if (gameItem) {
|
|
80319
|
+
return gameItem;
|
|
80320
|
+
}
|
|
80321
|
+
}
|
|
80322
|
+
const platformItem = await db.query.items.findFirst({
|
|
80323
|
+
where: and(eq(items.slug, slug), isNull(items.gameId))
|
|
80324
|
+
});
|
|
80325
|
+
if (!platformItem) {
|
|
80326
|
+
throw ApiError.notFound(`Item with slug '${slug}' not found`);
|
|
80327
|
+
}
|
|
80328
|
+
return platformItem;
|
|
80329
|
+
} catch (error2) {
|
|
80330
|
+
if (error2 instanceof ApiError) {
|
|
80331
|
+
throw error2;
|
|
80332
|
+
}
|
|
80333
|
+
logger2.error(`Error resolving item slug ${slug}:`, error2);
|
|
80334
|
+
throw ApiError.internal("Internal server error", error2);
|
|
80335
|
+
}
|
|
80336
|
+
}
|
|
80337
|
+
async function listGameItems(ctx) {
|
|
80338
|
+
const user = ctx.user;
|
|
80339
|
+
const gameId = ctx.params.gameId;
|
|
80340
|
+
if (!user) {
|
|
80341
|
+
throw ApiError.unauthorized("Must be logged in to view items");
|
|
80342
|
+
}
|
|
80343
|
+
if (!gameId) {
|
|
80344
|
+
throw ApiError.badRequest("Missing game ID");
|
|
80345
|
+
}
|
|
80346
|
+
try {
|
|
80347
|
+
const db = getDatabase();
|
|
80348
|
+
const gameItems = await db.query.items.findMany({
|
|
80349
|
+
where: eq(items.gameId, gameId)
|
|
80350
|
+
});
|
|
80351
|
+
return gameItems;
|
|
80352
|
+
} catch (error2) {
|
|
80353
|
+
if (error2 instanceof ApiError) {
|
|
80354
|
+
throw error2;
|
|
80355
|
+
}
|
|
80356
|
+
logger2.error(`Error fetching items for game ${gameId}:`, error2);
|
|
80357
|
+
throw ApiError.internal("Internal server error", error2);
|
|
80358
|
+
}
|
|
80359
|
+
}
|
|
80360
|
+
async function createGameItem(ctx) {
|
|
80361
|
+
const user = ctx.user;
|
|
80362
|
+
const gameId = ctx.params.gameId;
|
|
80363
|
+
if (!user) {
|
|
80364
|
+
throw ApiError.unauthorized("Must be logged in to create items");
|
|
80365
|
+
}
|
|
80366
|
+
if (!gameId) {
|
|
80367
|
+
throw ApiError.badRequest("Missing game ID");
|
|
80368
|
+
}
|
|
80369
|
+
await validateGameOwnership(user, gameId);
|
|
80370
|
+
let inputData;
|
|
80371
|
+
try {
|
|
80372
|
+
const requestBody = await ctx.request.json();
|
|
80373
|
+
const bodyData = typeof requestBody === "object" && requestBody !== null ? requestBody : {};
|
|
80374
|
+
const validationResult = InsertItemSchema.safeParse({
|
|
80375
|
+
...bodyData,
|
|
80376
|
+
gameId
|
|
80377
|
+
});
|
|
80378
|
+
if (!validationResult.success) {
|
|
80379
|
+
throw ApiError.badRequest(`Validation failed: ${formatValidationErrors(validationResult.error)}`);
|
|
80380
|
+
}
|
|
80381
|
+
inputData = validationResult.data;
|
|
80382
|
+
} catch (error2) {
|
|
80383
|
+
if (error2 instanceof ApiError) {
|
|
80384
|
+
throw error2;
|
|
80385
|
+
}
|
|
80386
|
+
logger2.error("Failed to parse request body or invalid JSON:", error2);
|
|
80387
|
+
throw ApiError.badRequest("Invalid JSON body");
|
|
80388
|
+
}
|
|
80389
|
+
try {
|
|
80390
|
+
const db = getDatabase();
|
|
80391
|
+
const [newItem] = await db.insert(items).values(inputData).returning();
|
|
80392
|
+
if (!newItem) {
|
|
80393
|
+
throw ApiError.internal("Failed to create item in database");
|
|
80394
|
+
}
|
|
80395
|
+
return newItem;
|
|
80396
|
+
} catch (error2) {
|
|
80397
|
+
if (error2 instanceof Error) {
|
|
80398
|
+
if (error2.message.includes("duplicate key value violates unique constraint")) {
|
|
80399
|
+
throw ApiError.conflict(`An item with slug '${inputData.slug}' already exists for this game`);
|
|
80400
|
+
}
|
|
80401
|
+
}
|
|
80402
|
+
logger2.error("Error creating game item:", error2);
|
|
80403
|
+
throw ApiError.internal("Internal server error", error2);
|
|
80404
|
+
}
|
|
80405
|
+
}
|
|
80406
|
+
async function updateGameItem(ctx) {
|
|
80407
|
+
const user = ctx.user;
|
|
80408
|
+
const gameId = ctx.params.gameId;
|
|
80409
|
+
const itemId = ctx.params.itemId;
|
|
80410
|
+
if (!user) {
|
|
80411
|
+
throw ApiError.unauthorized("Must be logged in to update items");
|
|
80412
|
+
}
|
|
80413
|
+
if (!gameId || !itemId) {
|
|
80414
|
+
throw ApiError.badRequest("Missing game ID or item ID");
|
|
80415
|
+
}
|
|
80416
|
+
await validateGameOwnership(user, gameId);
|
|
80417
|
+
let inputData;
|
|
80418
|
+
try {
|
|
80419
|
+
const requestBody = await ctx.request.json();
|
|
80420
|
+
const validationResult = UpdateItemSchema.safeParse(requestBody);
|
|
80421
|
+
if (!validationResult.success) {
|
|
80422
|
+
throw ApiError.badRequest(`Validation failed: ${formatValidationErrors(validationResult.error)}`);
|
|
80423
|
+
}
|
|
80424
|
+
inputData = validationResult.data;
|
|
80425
|
+
} catch (error2) {
|
|
80426
|
+
if (error2 instanceof ApiError) {
|
|
80427
|
+
throw error2;
|
|
80428
|
+
}
|
|
80429
|
+
logger2.error("Failed to parse request body or invalid JSON:", error2);
|
|
80430
|
+
throw ApiError.badRequest("Invalid JSON body");
|
|
80431
|
+
}
|
|
80432
|
+
if (Object.keys(inputData).length === 0) {
|
|
80433
|
+
throw ApiError.badRequest("No update data provided");
|
|
80434
|
+
}
|
|
80435
|
+
try {
|
|
80436
|
+
const db = getDatabase();
|
|
80437
|
+
const existingItem = await db.query.items.findFirst({
|
|
80438
|
+
where: and(eq(items.id, itemId), eq(items.gameId, gameId))
|
|
80439
|
+
});
|
|
80440
|
+
if (!existingItem) {
|
|
80441
|
+
throw ApiError.notFound("Item not found for this game");
|
|
80442
|
+
}
|
|
80443
|
+
const [updatedItem] = await db.update(items).set(inputData).where(eq(items.id, itemId)).returning();
|
|
80444
|
+
if (!updatedItem) {
|
|
80445
|
+
throw ApiError.notFound("Item not found for update");
|
|
80446
|
+
}
|
|
80447
|
+
return updatedItem;
|
|
80448
|
+
} catch (error2) {
|
|
80449
|
+
if (error2 instanceof Error) {
|
|
80450
|
+
if (error2.message.includes("duplicate key value violates unique constraint")) {
|
|
80451
|
+
throw ApiError.conflict("An item with this slug already exists for this game");
|
|
80452
|
+
}
|
|
80453
|
+
}
|
|
80454
|
+
logger2.error(`Error updating game item ${itemId}:`, error2);
|
|
80455
|
+
throw ApiError.internal("Internal server error", error2);
|
|
80456
|
+
}
|
|
80457
|
+
}
|
|
80458
|
+
async function deleteGameItem(ctx) {
|
|
80459
|
+
const user = ctx.user;
|
|
80460
|
+
const gameId = ctx.params.gameId;
|
|
80461
|
+
const itemId = ctx.params.itemId;
|
|
80462
|
+
if (!user) {
|
|
80463
|
+
throw ApiError.unauthorized("Must be logged in to delete items");
|
|
80464
|
+
}
|
|
80465
|
+
if (!gameId || !itemId) {
|
|
80466
|
+
throw ApiError.badRequest("Missing game ID or item ID");
|
|
80467
|
+
}
|
|
80468
|
+
await validateGameOwnership(user, gameId);
|
|
80469
|
+
try {
|
|
80470
|
+
const db = getDatabase();
|
|
80471
|
+
const result = await db.delete(items).where(and(eq(items.id, itemId), eq(items.gameId, gameId))).returning({ id: items.id });
|
|
80472
|
+
if (result.length === 0) {
|
|
80473
|
+
throw ApiError.notFound("Item not found for this game");
|
|
80474
|
+
}
|
|
80475
|
+
} catch (error2) {
|
|
80476
|
+
if (error2 instanceof ApiError) {
|
|
80477
|
+
throw error2;
|
|
80478
|
+
}
|
|
80479
|
+
logger2.error(`Error deleting game item ${itemId}:`, error2);
|
|
80480
|
+
throw ApiError.internal("Internal server error", error2);
|
|
80481
|
+
}
|
|
80482
|
+
}
|
|
80483
|
+
// ../../node_modules/@oslojs/binary/dist/uint.js
|
|
80484
|
+
class BigEndian {
|
|
80485
|
+
uint8(data2, offset) {
|
|
80486
|
+
if (data2.byteLength < offset + 1) {
|
|
80487
|
+
throw new TypeError("Insufficient bytes");
|
|
80488
|
+
}
|
|
80489
|
+
return data2[offset];
|
|
80490
|
+
}
|
|
80491
|
+
uint16(data2, offset) {
|
|
80492
|
+
if (data2.byteLength < offset + 2) {
|
|
80493
|
+
throw new TypeError("Insufficient bytes");
|
|
80494
|
+
}
|
|
80495
|
+
return data2[offset] << 8 | data2[offset + 1];
|
|
80496
|
+
}
|
|
80497
|
+
uint32(data2, offset) {
|
|
80498
|
+
if (data2.byteLength < offset + 4) {
|
|
80499
|
+
throw new TypeError("Insufficient bytes");
|
|
80500
|
+
}
|
|
80501
|
+
let result = 0;
|
|
80502
|
+
for (let i3 = 0;i3 < 4; i3++) {
|
|
80503
|
+
result |= data2[offset + i3] << 24 - i3 * 8;
|
|
80504
|
+
}
|
|
80505
|
+
return result;
|
|
80506
|
+
}
|
|
80507
|
+
uint64(data2, offset) {
|
|
80508
|
+
if (data2.byteLength < offset + 8) {
|
|
80509
|
+
throw new TypeError("Insufficient bytes");
|
|
80510
|
+
}
|
|
80511
|
+
let result = 0n;
|
|
80512
|
+
for (let i3 = 0;i3 < 8; i3++) {
|
|
80513
|
+
result |= BigInt(data2[offset + i3]) << BigInt(56 - i3 * 8);
|
|
80514
|
+
}
|
|
80515
|
+
return result;
|
|
80516
|
+
}
|
|
80517
|
+
putUint8(target, value, offset) {
|
|
80518
|
+
if (target.length < offset + 1) {
|
|
80519
|
+
throw new TypeError("Not enough space");
|
|
80520
|
+
}
|
|
80521
|
+
if (value < 0 || value > 255) {
|
|
80522
|
+
throw new TypeError("Invalid uint8 value");
|
|
80523
|
+
}
|
|
80524
|
+
target[offset] = value;
|
|
80525
|
+
}
|
|
80526
|
+
putUint16(target, value, offset) {
|
|
80527
|
+
if (target.length < offset + 2) {
|
|
80528
|
+
throw new TypeError("Not enough space");
|
|
80529
|
+
}
|
|
80530
|
+
if (value < 0 || value > 65535) {
|
|
80531
|
+
throw new TypeError("Invalid uint16 value");
|
|
80532
|
+
}
|
|
80533
|
+
target[offset] = value >> 8;
|
|
80534
|
+
target[offset + 1] = value & 255;
|
|
80535
|
+
}
|
|
80536
|
+
putUint32(target, value, offset) {
|
|
80537
|
+
if (target.length < offset + 4) {
|
|
80538
|
+
throw new TypeError("Not enough space");
|
|
80539
|
+
}
|
|
80540
|
+
if (value < 0 || value > 4294967295) {
|
|
80541
|
+
throw new TypeError("Invalid uint32 value");
|
|
80542
|
+
}
|
|
80543
|
+
for (let i3 = 0;i3 < 4; i3++) {
|
|
80544
|
+
target[offset + i3] = value >> (3 - i3) * 8 & 255;
|
|
80545
|
+
}
|
|
80546
|
+
}
|
|
80547
|
+
putUint64(target, value, offset) {
|
|
80548
|
+
if (target.length < offset + 8) {
|
|
80549
|
+
throw new TypeError("Not enough space");
|
|
80550
|
+
}
|
|
80551
|
+
if (value < 0 || value > 18446744073709551615n) {
|
|
80552
|
+
throw new TypeError("Invalid uint64 value");
|
|
80553
|
+
}
|
|
80554
|
+
for (let i3 = 0;i3 < 8; i3++) {
|
|
80555
|
+
target[offset + i3] = Number(value >> BigInt((7 - i3) * 8) & 0xffn);
|
|
80556
|
+
}
|
|
80557
|
+
}
|
|
80558
|
+
}
|
|
80559
|
+
|
|
80560
|
+
class LittleEndian {
|
|
80561
|
+
uint8(data2, offset) {
|
|
80562
|
+
if (data2.byteLength < offset + 1) {
|
|
80563
|
+
throw new TypeError("Insufficient bytes");
|
|
80564
|
+
}
|
|
80565
|
+
return data2[offset];
|
|
80566
|
+
}
|
|
80567
|
+
uint16(data2, offset) {
|
|
80568
|
+
if (data2.byteLength < offset + 2) {
|
|
80569
|
+
throw new TypeError("Insufficient bytes");
|
|
80570
|
+
}
|
|
80571
|
+
return data2[offset] | data2[offset + 1] << 8;
|
|
80572
|
+
}
|
|
80573
|
+
uint32(data2, offset) {
|
|
80574
|
+
if (data2.byteLength < offset + 4) {
|
|
80575
|
+
throw new TypeError("Insufficient bytes");
|
|
80576
|
+
}
|
|
80577
|
+
let result = 0;
|
|
80578
|
+
for (let i3 = 0;i3 < 4; i3++) {
|
|
80579
|
+
result |= data2[offset + i3] << i3 * 8;
|
|
80580
|
+
}
|
|
80581
|
+
return result;
|
|
80582
|
+
}
|
|
80583
|
+
uint64(data2, offset) {
|
|
80584
|
+
if (data2.byteLength < offset + 8) {
|
|
80585
|
+
throw new TypeError("Insufficient bytes");
|
|
80586
|
+
}
|
|
80587
|
+
let result = 0n;
|
|
80588
|
+
for (let i3 = 0;i3 < 8; i3++) {
|
|
80589
|
+
result |= BigInt(data2[offset + i3]) << BigInt(i3 * 8);
|
|
80590
|
+
}
|
|
80591
|
+
return result;
|
|
80592
|
+
}
|
|
80593
|
+
putUint8(target, value, offset) {
|
|
80594
|
+
if (target.length < 1 + offset) {
|
|
80595
|
+
throw new TypeError("Insufficient space");
|
|
80596
|
+
}
|
|
80597
|
+
if (value < 0 || value > 255) {
|
|
80598
|
+
throw new TypeError("Invalid uint8 value");
|
|
80599
|
+
}
|
|
80600
|
+
target[offset] = value;
|
|
80601
|
+
}
|
|
80602
|
+
putUint16(target, value, offset) {
|
|
80603
|
+
if (target.length < 2 + offset) {
|
|
80604
|
+
throw new TypeError("Insufficient space");
|
|
80605
|
+
}
|
|
80606
|
+
if (value < 0 || value > 65535) {
|
|
80607
|
+
throw new TypeError("Invalid uint16 value");
|
|
80608
|
+
}
|
|
80609
|
+
target[offset + 1] = value >> 8;
|
|
80610
|
+
target[offset] = value & 255;
|
|
80611
|
+
}
|
|
80612
|
+
putUint32(target, value, offset) {
|
|
80613
|
+
if (target.length < 4 + offset) {
|
|
80614
|
+
throw new TypeError("Insufficient space");
|
|
80615
|
+
}
|
|
80616
|
+
if (value < 0 || value > 4294967295) {
|
|
80617
|
+
throw new TypeError("Invalid uint32 value");
|
|
80618
|
+
}
|
|
80619
|
+
for (let i3 = 0;i3 < 4; i3++) {
|
|
80620
|
+
target[offset + i3] = value >> i3 * 8 & 255;
|
|
80228
80621
|
}
|
|
80229
80622
|
}
|
|
80230
80623
|
putUint64(target, value, offset) {
|
|
@@ -81166,6 +81559,88 @@ gamesRouter.post("/uploads/finalize", async (c2) => {
|
|
|
81166
81559
|
return c2.json({ error: message2 }, 500);
|
|
81167
81560
|
}
|
|
81168
81561
|
});
|
|
81562
|
+
gamesRouter.get("/:gameId/items", async (c2) => {
|
|
81563
|
+
const gameId = c2.req.param("gameId");
|
|
81564
|
+
const ctx = {
|
|
81565
|
+
user: c2.get("user"),
|
|
81566
|
+
params: { gameId },
|
|
81567
|
+
url: new URL(c2.req.url),
|
|
81568
|
+
request: c2.req.raw
|
|
81569
|
+
};
|
|
81570
|
+
try {
|
|
81571
|
+
const result = await listGameItems(ctx);
|
|
81572
|
+
return c2.json(result);
|
|
81573
|
+
} catch (error2) {
|
|
81574
|
+
if (error2 instanceof ApiError) {
|
|
81575
|
+
return c2.json({ error: error2.message }, error2.statusCode);
|
|
81576
|
+
}
|
|
81577
|
+
console.error("Error in listGameItems:", error2);
|
|
81578
|
+
const message2 = error2 instanceof Error ? error2.message : "Internal server error";
|
|
81579
|
+
return c2.json({ error: message2 }, 500);
|
|
81580
|
+
}
|
|
81581
|
+
});
|
|
81582
|
+
gamesRouter.post("/:gameId/items", async (c2) => {
|
|
81583
|
+
const gameId = c2.req.param("gameId");
|
|
81584
|
+
const ctx = {
|
|
81585
|
+
user: c2.get("user"),
|
|
81586
|
+
params: { gameId },
|
|
81587
|
+
url: new URL(c2.req.url),
|
|
81588
|
+
request: c2.req.raw
|
|
81589
|
+
};
|
|
81590
|
+
try {
|
|
81591
|
+
const result = await createGameItem(ctx);
|
|
81592
|
+
return c2.json(result, 201);
|
|
81593
|
+
} catch (error2) {
|
|
81594
|
+
if (error2 instanceof ApiError) {
|
|
81595
|
+
return c2.json({ error: error2.message }, error2.statusCode);
|
|
81596
|
+
}
|
|
81597
|
+
console.error("Error in createGameItem:", error2);
|
|
81598
|
+
const message2 = error2 instanceof Error ? error2.message : "Internal server error";
|
|
81599
|
+
return c2.json({ error: message2 }, 500);
|
|
81600
|
+
}
|
|
81601
|
+
});
|
|
81602
|
+
gamesRouter.patch("/:gameId/items/:itemId", async (c2) => {
|
|
81603
|
+
const gameId = c2.req.param("gameId");
|
|
81604
|
+
const itemId = c2.req.param("itemId");
|
|
81605
|
+
const ctx = {
|
|
81606
|
+
user: c2.get("user"),
|
|
81607
|
+
params: { gameId, itemId },
|
|
81608
|
+
url: new URL(c2.req.url),
|
|
81609
|
+
request: c2.req.raw
|
|
81610
|
+
};
|
|
81611
|
+
try {
|
|
81612
|
+
const result = await updateGameItem(ctx);
|
|
81613
|
+
return c2.json(result);
|
|
81614
|
+
} catch (error2) {
|
|
81615
|
+
if (error2 instanceof ApiError) {
|
|
81616
|
+
return c2.json({ error: error2.message }, error2.statusCode);
|
|
81617
|
+
}
|
|
81618
|
+
console.error("Error in updateGameItem:", error2);
|
|
81619
|
+
const message2 = error2 instanceof Error ? error2.message : "Internal server error";
|
|
81620
|
+
return c2.json({ error: message2 }, 500);
|
|
81621
|
+
}
|
|
81622
|
+
});
|
|
81623
|
+
gamesRouter.delete("/:gameId/items/:itemId", async (c2) => {
|
|
81624
|
+
const gameId = c2.req.param("gameId");
|
|
81625
|
+
const itemId = c2.req.param("itemId");
|
|
81626
|
+
const ctx = {
|
|
81627
|
+
user: c2.get("user"),
|
|
81628
|
+
params: { gameId, itemId },
|
|
81629
|
+
url: new URL(c2.req.url),
|
|
81630
|
+
request: c2.req.raw
|
|
81631
|
+
};
|
|
81632
|
+
try {
|
|
81633
|
+
await deleteGameItem(ctx);
|
|
81634
|
+
return c2.body(null, 204);
|
|
81635
|
+
} catch (error2) {
|
|
81636
|
+
if (error2 instanceof ApiError) {
|
|
81637
|
+
return c2.json({ error: error2.message }, error2.statusCode);
|
|
81638
|
+
}
|
|
81639
|
+
console.error("Error in deleteGameItem:", error2);
|
|
81640
|
+
const message2 = error2 instanceof Error ? error2.message : "Internal server error";
|
|
81641
|
+
return c2.json({ error: message2 }, 500);
|
|
81642
|
+
}
|
|
81643
|
+
});
|
|
81169
81644
|
// src/routes/manifest.ts
|
|
81170
81645
|
var manifestRouter = new Hono2;
|
|
81171
81646
|
manifestRouter.get("/", async (c2) => {
|
|
@@ -81297,157 +81772,27 @@ shopRouter.get("/view", async (c2) => {
|
|
|
81297
81772
|
return c2.json({ error: message2 }, 500);
|
|
81298
81773
|
}
|
|
81299
81774
|
});
|
|
81300
|
-
//
|
|
81301
|
-
|
|
81302
|
-
|
|
81303
|
-
|
|
81304
|
-
|
|
81305
|
-
|
|
81306
|
-
|
|
81307
|
-
|
|
81308
|
-
|
|
81309
|
-
return allItems;
|
|
81310
|
-
} catch (error2) {
|
|
81311
|
-
if (error2 instanceof ApiError) {
|
|
81312
|
-
throw error2;
|
|
81313
|
-
}
|
|
81314
|
-
logger2.error("Error fetching items:", error2);
|
|
81315
|
-
throw ApiError.internal("Internal server error", error2);
|
|
81316
|
-
}
|
|
81317
|
-
}
|
|
81318
|
-
async function getItemById(ctx) {
|
|
81319
|
-
const user = ctx.user;
|
|
81320
|
-
const itemId = ctx.params.itemId;
|
|
81321
|
-
if (!user) {
|
|
81322
|
-
throw ApiError.unauthorized("Must be logged in to view item details");
|
|
81323
|
-
}
|
|
81324
|
-
if (!itemId) {
|
|
81325
|
-
throw ApiError.badRequest("Missing item ID");
|
|
81326
|
-
}
|
|
81327
|
-
try {
|
|
81328
|
-
const db = getDatabase();
|
|
81329
|
-
const item = await db.query.items.findFirst({
|
|
81330
|
-
where: eq(items.id, itemId)
|
|
81331
|
-
});
|
|
81332
|
-
if (!item) {
|
|
81333
|
-
throw ApiError.notFound("Item not found");
|
|
81334
|
-
}
|
|
81335
|
-
return item;
|
|
81336
|
-
} catch (error2) {
|
|
81337
|
-
if (error2 instanceof ApiError) {
|
|
81338
|
-
throw error2;
|
|
81339
|
-
}
|
|
81340
|
-
logger2.error(`Error fetching item ${itemId}:`, error2);
|
|
81341
|
-
throw ApiError.internal("Internal server error", error2);
|
|
81342
|
-
}
|
|
81343
|
-
}
|
|
81344
|
-
async function createItem(ctx) {
|
|
81345
|
-
const user = ctx.user;
|
|
81346
|
-
if (!user || user.role !== "admin") {
|
|
81347
|
-
throw ApiError.forbidden("Admin access required");
|
|
81348
|
-
}
|
|
81349
|
-
let inputData;
|
|
81350
|
-
try {
|
|
81351
|
-
const requestBody = await ctx.request.json();
|
|
81352
|
-
const validationResult = InsertItemSchema.safeParse(requestBody);
|
|
81353
|
-
if (!validationResult.success) {
|
|
81354
|
-
throw ApiError.badRequest(`Validation failed: ${formatValidationErrors(validationResult.error)}`);
|
|
81355
|
-
}
|
|
81356
|
-
inputData = validationResult.data;
|
|
81357
|
-
} catch (error2) {
|
|
81358
|
-
if (error2 instanceof ApiError) {
|
|
81359
|
-
throw error2;
|
|
81360
|
-
}
|
|
81361
|
-
logger2.error("Failed to parse request body or invalid JSON:", error2);
|
|
81362
|
-
throw ApiError.badRequest("Invalid JSON body");
|
|
81363
|
-
}
|
|
81364
|
-
try {
|
|
81365
|
-
const db = getDatabase();
|
|
81366
|
-
const [newItem] = await db.insert(items).values(inputData).returning();
|
|
81367
|
-
if (!newItem) {
|
|
81368
|
-
throw ApiError.internal("Failed to create item in database");
|
|
81369
|
-
}
|
|
81370
|
-
return newItem;
|
|
81371
|
-
} catch (error2) {
|
|
81372
|
-
if (error2 instanceof Error) {
|
|
81373
|
-
if (error2.message.includes("duplicate key value violates unique constraint")) {
|
|
81374
|
-
throw ApiError.conflict("An item with this internal name already exists");
|
|
81375
|
-
}
|
|
81376
|
-
}
|
|
81377
|
-
logger2.error("Error creating item:", error2);
|
|
81378
|
-
throw ApiError.internal("Internal server error", error2);
|
|
81379
|
-
}
|
|
81380
|
-
}
|
|
81381
|
-
async function updateItem(ctx) {
|
|
81382
|
-
const user = ctx.user;
|
|
81383
|
-
const itemId = ctx.params.itemId;
|
|
81384
|
-
if (!user || user.role !== "admin") {
|
|
81385
|
-
throw ApiError.forbidden("Admin access required");
|
|
81386
|
-
}
|
|
81387
|
-
if (!itemId) {
|
|
81388
|
-
throw ApiError.badRequest("Missing item ID");
|
|
81389
|
-
}
|
|
81390
|
-
let inputData;
|
|
81391
|
-
try {
|
|
81392
|
-
const requestBody = await ctx.request.json();
|
|
81393
|
-
const validationResult = UpdateItemSchema.safeParse(requestBody);
|
|
81394
|
-
if (!validationResult.success) {
|
|
81395
|
-
throw ApiError.badRequest(`Validation failed: ${formatValidationErrors(validationResult.error)}`);
|
|
81396
|
-
}
|
|
81397
|
-
inputData = validationResult.data;
|
|
81398
|
-
} catch (error2) {
|
|
81399
|
-
if (error2 instanceof ApiError) {
|
|
81400
|
-
throw error2;
|
|
81401
|
-
}
|
|
81402
|
-
logger2.error("Failed to parse request body or invalid JSON:", error2);
|
|
81403
|
-
throw ApiError.badRequest("Invalid JSON body");
|
|
81404
|
-
}
|
|
81405
|
-
if (Object.keys(inputData).length === 0) {
|
|
81406
|
-
throw ApiError.badRequest("No update data provided");
|
|
81407
|
-
}
|
|
81408
|
-
try {
|
|
81409
|
-
const db = getDatabase();
|
|
81410
|
-
const [updatedItem] = await db.update(items).set(inputData).where(eq(items.id, itemId)).returning();
|
|
81411
|
-
if (!updatedItem) {
|
|
81412
|
-
throw ApiError.notFound("Item not found for update");
|
|
81413
|
-
}
|
|
81414
|
-
return updatedItem;
|
|
81415
|
-
} catch (error2) {
|
|
81416
|
-
if (error2 instanceof Error) {
|
|
81417
|
-
if (error2.message.includes("duplicate key value violates unique constraint")) {
|
|
81418
|
-
throw ApiError.conflict("An item with this internal name already exists");
|
|
81419
|
-
}
|
|
81420
|
-
}
|
|
81421
|
-
logger2.error(`Error updating item ${itemId}:`, error2);
|
|
81422
|
-
throw ApiError.internal("Internal server error", error2);
|
|
81423
|
-
}
|
|
81424
|
-
}
|
|
81425
|
-
async function deleteItem(ctx) {
|
|
81426
|
-
const user = ctx.user;
|
|
81427
|
-
const itemId = ctx.params.itemId;
|
|
81428
|
-
if (!user || user.role !== "admin") {
|
|
81429
|
-
throw ApiError.forbidden("Admin access required");
|
|
81430
|
-
}
|
|
81431
|
-
if (!itemId) {
|
|
81432
|
-
throw ApiError.badRequest("Missing item ID");
|
|
81433
|
-
}
|
|
81775
|
+
// src/routes/items.ts
|
|
81776
|
+
var itemsRouter = new Hono2;
|
|
81777
|
+
itemsRouter.get("/resolve", async (c2) => {
|
|
81778
|
+
const ctx = {
|
|
81779
|
+
user: c2.get("user"),
|
|
81780
|
+
params: {},
|
|
81781
|
+
url: new URL(c2.req.url),
|
|
81782
|
+
request: c2.req.raw
|
|
81783
|
+
};
|
|
81434
81784
|
try {
|
|
81435
|
-
const
|
|
81436
|
-
|
|
81437
|
-
if (result.length === 0) {
|
|
81438
|
-
throw ApiError.notFound("Item not found for deletion");
|
|
81439
|
-
}
|
|
81785
|
+
const result = await resolveItem(ctx);
|
|
81786
|
+
return c2.json(result);
|
|
81440
81787
|
} catch (error2) {
|
|
81441
81788
|
if (error2 instanceof ApiError) {
|
|
81442
|
-
|
|
81789
|
+
return c2.json({ error: error2.message }, error2.statusCode);
|
|
81443
81790
|
}
|
|
81444
|
-
|
|
81445
|
-
|
|
81791
|
+
console.error("Error in resolveItem:", error2);
|
|
81792
|
+
const message2 = error2 instanceof Error ? error2.message : "Internal server error";
|
|
81793
|
+
return c2.json({ error: message2 }, 500);
|
|
81446
81794
|
}
|
|
81447
|
-
}
|
|
81448
|
-
|
|
81449
|
-
// src/routes/items.ts
|
|
81450
|
-
var itemsRouter = new Hono2;
|
|
81795
|
+
});
|
|
81451
81796
|
itemsRouter.post("/", async (c2) => {
|
|
81452
81797
|
const ctx = {
|
|
81453
81798
|
user: c2.get("user"),
|
|
@@ -81620,8 +81965,8 @@ async function createCurrency(ctx) {
|
|
|
81620
81965
|
} catch (error2) {
|
|
81621
81966
|
if (error2 instanceof Error) {
|
|
81622
81967
|
if (error2.message.includes("duplicate key value violates unique constraint") || error2.message.includes("UNIQUE constraint failed")) {
|
|
81623
|
-
if (error2.message.includes("
|
|
81624
|
-
throw ApiError.conflict("A currency with this
|
|
81968
|
+
if (error2.message.includes("currencies_slug_unique")) {
|
|
81969
|
+
throw ApiError.conflict("A currency with this slug already exists");
|
|
81625
81970
|
}
|
|
81626
81971
|
throw ApiError.conflict("A similar currency already exists");
|
|
81627
81972
|
}
|
|
@@ -81667,8 +82012,8 @@ async function updateCurrency(ctx) {
|
|
|
81667
82012
|
} catch (error2) {
|
|
81668
82013
|
if (error2 instanceof Error) {
|
|
81669
82014
|
if (error2.message.includes("duplicate key value violates unique constraint") || error2.message.includes("UNIQUE constraint failed")) {
|
|
81670
|
-
if (error2.message.includes("
|
|
81671
|
-
throw ApiError.conflict("A currency with this
|
|
82015
|
+
if (error2.message.includes("currencies_slug_unique")) {
|
|
82016
|
+
throw ApiError.conflict("A currency with this slug already exists");
|
|
81672
82017
|
}
|
|
81673
82018
|
throw ApiError.conflict("A similar currency already exists");
|
|
81674
82019
|
}
|