diff --git a/backend/src/lib/handlers/arsenal.ts b/backend/src/lib/handlers/arsenal.ts new file mode 100644 index 0000000..22c92e1 --- /dev/null +++ b/backend/src/lib/handlers/arsenal.ts @@ -0,0 +1,81 @@ +import { route, HandlerContext, HttpError } from '@/lib/router'; +import { getDb } from '@/lib/db'; + +route('player/:steamId/arsenal_loadouts', ['GET'], (ctx: HandlerContext) => { + const db = getDb(); + const rows = db.prepare('SELECT * FROM arsenal_loadouts WHERE steam_id = ?').all(ctx.params.steamId); + const loadouts: Record = {}; + for (const r of rows as any[]) { + loadouts[r.hero_name] = JSON.parse(r.loadout); + } + return { arsenal_loadouts: loadouts }; +}); + +route('player/:steamId/arsenal_loadouts', ['PUT'], (ctx: HandlerContext) => { + const { arsenal_loadouts } = ctx.body as any; + if (!arsenal_loadouts) throw new HttpError(400, 'arsenal_loadouts required'); + const db = getDb(); + const upsert = db.prepare(` + INSERT INTO arsenal_loadouts (steam_id, hero_name, loadout, updated_at) VALUES (?, ?, ?, datetime('now')) + ON CONFLICT(steam_id, hero_name) DO UPDATE SET loadout = ?, updated_at = datetime('now') + `); + const tx = db.transaction(() => { + for (const [hero, loadout] of Object.entries(arsenal_loadouts)) { + upsert.run(ctx.params.steamId, hero, JSON.stringify(loadout), JSON.stringify(loadout)); + } + }); + tx(); + return { success: true }; +}); + +route('player/:steamId/arsenal_inventory', ['GET'], (ctx: HandlerContext) => { + const db = getDb(); + const items = db.prepare('SELECT * FROM arsenal_inventory WHERE steam_id = ?').all(ctx.params.steamId); + const instances: Record = {}; + for (const item of items as any[]) { + instances[item.instance_id] = { + instanceId: item.instance_id, + itemName: item.item_name, + quality: item.quality, + upgradeLevel: item.upgrade_level, + serial: item.serial, + globalSerial: item.global_serial, + ownerName: item.owner_name, + pinned: !!item.pinned, + favorite: !!item.favorite, + stats: JSON.parse(item.stats || '[]'), + }; + } + return { arsenal_inventory: { instances } }; +}); + +route('player/:steamId/arsenal_inventory', ['PUT'], (ctx: HandlerContext) => { + const { arsenal_inventory } = ctx.body as any; + if (!arsenal_inventory || !arsenal_inventory.instances) throw new HttpError(400, 'arsenal_inventory.instances required'); + const db = getDb(); + const upsert = db.prepare(` + INSERT INTO arsenal_inventory (steam_id, instance_id, item_name, quality, upgrade_level, serial, global_serial, owner_name, pinned, favorite, stats) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(steam_id, instance_id) DO UPDATE SET + item_name = excluded.item_name, quality = excluded.quality, upgrade_level = excluded.upgrade_level, + serial = excluded.serial, global_serial = excluded.global_serial, owner_name = excluded.owner_name, + pinned = excluded.pinned, favorite = excluded.favorite, stats = excluded.stats + `); + const tx = db.transaction(() => { + for (const [instId, inst] of Object.entries(arsenal_inventory.instances)) { + const i = inst as any; + upsert.run(ctx.params.steamId, instId, + i.itemName || i.item_name || '', + i.quality || 'common', + i.upgradeLevel || i.upgrade_level || 0, + i.serial || 0, + i.globalSerial || i.global_serial || 0, + i.ownerName || i.owner_name || '', + i.pinned ? 1 : 0, + i.favorite ? 1 : 0, + JSON.stringify(i.stats || [])); + } + }); + tx(); + return { success: true }; +}); diff --git a/backend/src/lib/handlers/contracts.ts b/backend/src/lib/handlers/contracts.ts new file mode 100644 index 0000000..f8e7eb7 --- /dev/null +++ b/backend/src/lib/handlers/contracts.ts @@ -0,0 +1,18 @@ +import { route, HandlerContext, HttpError } from '@/lib/router'; +import { getDb } from '@/lib/db'; + +route('player/:steamId/death_sentence_contracts', ['GET'], (ctx: HandlerContext) => { + const db = getDb(); + const row = db.prepare('SELECT contracts FROM death_sentence_contracts WHERE steam_id = ?').get(ctx.params.steamId) as any; + return { death_sentence_contracts: row ? JSON.parse(row.contracts) : { roster: [] } }; +}); + +route('player/:steamId/death_sentence_contracts', ['PUT'], (ctx: HandlerContext) => { + const { death_sentence_contracts } = ctx.body as any; + const db = getDb(); + db.prepare(` + INSERT INTO death_sentence_contracts (steam_id, contracts, updated_at) VALUES (?, ?, datetime('now')) + ON CONFLICT(steam_id) DO UPDATE SET contracts = ?, updated_at = datetime('now') + `).run(ctx.params.steamId, JSON.stringify(death_sentence_contracts || {}), JSON.stringify(death_sentence_contracts || {})); + return { success: true }; +}); diff --git a/backend/src/lib/handlers/marketplace.ts b/backend/src/lib/handlers/marketplace.ts new file mode 100644 index 0000000..7dca778 --- /dev/null +++ b/backend/src/lib/handlers/marketplace.ts @@ -0,0 +1,56 @@ +import { route, HandlerContext, HttpError } from '@/lib/router'; +import { getDb } from '@/lib/db'; + +route('arsenal_market/listings', ['GET'], (ctx: HandlerContext) => { + const db = getDb(); + return db.prepare("SELECT * FROM arsenal_market_listings WHERE status = 'active' ORDER BY created_at DESC").all(); +}); + +route('player/:steamId/arsenal_market/my_listings', ['GET'], (ctx: HandlerContext) => { + const db = getDb(); + return db.prepare("SELECT * FROM arsenal_market_listings WHERE steam_id = ? AND status = 'active' ORDER BY created_at DESC").all(ctx.params.steamId); +}); + +route('player/:steamId/arsenal_market/slots', ['GET'], (ctx: HandlerContext) => { + return { slots: 5, used: 0 }; +}); + +route('player/:steamId/arsenal_market/sales', ['GET'], (ctx: HandlerContext) => { + const db = getDb(); + return db.prepare('SELECT * FROM arsenal_market_sales WHERE seller_steam_id = ? ORDER BY created_at DESC').all(ctx.params.steamId); +}); + +route('player/:steamId/arsenal_market/create', ['POST'], (ctx: HandlerContext) => { + const { instance_id, item_name, quality, upgrade_level, serial, global_serial, price_free } = ctx.body as any; + const listingId = `list_${Date.now()}_${Math.floor(Math.random() * 1000)}`; + const db = getDb(); + db.prepare(` + INSERT INTO arsenal_market_listings (listing_id, steam_id, instance_id, item_name, quality, upgrade_level, serial, global_serial, price_free, status) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'active') + `).run(listingId, ctx.params.steamId, instance_id || '', item_name || 'Unknown', quality || 'common', + upgrade_level || 0, serial || 0, global_serial || 0, price_free || 0); + return { success: true, listing_id: listingId }; +}); + +route('player/:steamId/arsenal_market/buy', ['POST'], (ctx: HandlerContext) => { + const { listing_id } = ctx.body as any; + if (!listing_id) throw new HttpError(400, 'listing_id required'); + const db = getDb(); + const listing = db.prepare('SELECT * FROM arsenal_market_listings WHERE listing_id = ?').get(listing_id) as any; + if (!listing) throw new HttpError(404, 'Listing not found'); + if (listing.status !== 'active') throw new HttpError(400, 'Listing not active'); + + db.prepare("UPDATE arsenal_market_listings SET status = 'sold' WHERE listing_id = ?").run(listing_id); + db.prepare('INSERT INTO arsenal_market_sales (listing_id, seller_steam_id, buyer_steam_id, item_name, price_free) VALUES (?, ?, ?, ?, ?)') + .run(listing_id, listing.steam_id, ctx.params.steamId, listing.item_name, listing.price_free); + return { success: true }; +}); + +route('player/:steamId/arsenal_market/cancel', ['POST'], (ctx: HandlerContext) => { + const { listing_id } = ctx.body as any; + if (!listing_id) throw new HttpError(400, 'listing_id required'); + const db = getDb(); + db.prepare("UPDATE arsenal_market_listings SET status = 'cancelled' WHERE listing_id = ? AND steam_id = ?") + .run(listing_id, ctx.params.steamId); + return { success: true }; +});