feat: add player handler with all endpoints

This commit is contained in:
achmad
2026-05-29 16:35:07 +07:00
parent 3fd15cf00b
commit 44784682ae
+172
View File
@@ -0,0 +1,172 @@
import { route, HandlerContext, HttpError } from '@/lib/router';
import { getDb } from '@/lib/db';
// POST /player — Create player profile
route('player', ['POST'], (ctx: HandlerContext) => {
const { steam_id, player_name } = ctx.body as any;
if (!steam_id) throw new HttpError(400, 'steam_id is required');
const db = getDb();
const existing = db.prepare('SELECT * FROM players WHERE steam_id = ?').get(steam_id) as any;
if (existing) {
return existing;
}
db.prepare('INSERT INTO players (steam_id, player_name) VALUES (?, ?)').run(steam_id, player_name || '');
try {
db.prepare('INSERT OR IGNORE INTO battle_passes (steam_id) VALUES (?)').run(steam_id);
} catch {}
const player = db.prepare('SELECT * FROM players WHERE steam_id = ?').get(steam_id);
return player;
});
// GET /player/:steamId — Get player profile
// Returns the player row plus recentGames array and stats object
route('player/:steamId', ['GET'], (ctx: HandlerContext) => {
const db = getDb();
const player = db.prepare('SELECT * FROM players WHERE steam_id = ?').get(ctx.params.steamId) as any;
if (!player) throw new HttpError(404, 'Player not found');
return {
...player,
recentGames: [],
stats: {
total_games: 0,
total_wins: 0,
rating: 0,
},
};
});
// GET /player/:steamId/history — Match history with limit/offset
route('player/:steamId/history', ['GET'], (ctx: HandlerContext) => {
const limit = parseInt(ctx.searchParams.get('limit') || '10');
const offset = parseInt(ctx.searchParams.get('offset') || '0');
const db = getDb();
const games = db.prepare(
'SELECT * FROM game_history WHERE steam_id = ? ORDER BY created_at DESC LIMIT ? OFFSET ?'
).all(ctx.params.steamId, limit, offset);
return games;
});
// GET /player/:steamId/currency — Get currency balances
route('player/:steamId/currency', ['GET'], (ctx: HandlerContext) => {
const db = getDb();
const player = db.prepare('SELECT free_currency, donate_currency, dust_currency FROM players WHERE steam_id = ?').get(ctx.params.steamId) as any;
if (!player) throw new HttpError(404, 'Player not found');
return player;
});
// PUT /player/:steamId/currency — Save currency balances
route('player/:steamId/currency', ['PUT'], (ctx: HandlerContext) => {
const { free_currency, donate_currency, dust_currency } = ctx.body as any;
const db = getDb();
const player = db.prepare('SELECT * FROM players WHERE steam_id = ?').get(ctx.params.steamId) as any;
if (!player) throw new HttpError(404, 'Player not found');
db.prepare(`
UPDATE players SET free_currency = ?, donate_currency = ?, dust_currency = ?, updated_at = datetime('now')
WHERE steam_id = ?
`).run(
free_currency ?? player.free_currency,
donate_currency ?? player.donate_currency,
dust_currency ?? player.dust_currency,
ctx.params.steamId
);
return { success: true };
});
// POST /player/:steamId/currency/give — Grant currency (used by BP rewards)
route('player/:steamId/currency/give', ['POST'], (ctx: HandlerContext) => {
const body = ctx.body as any;
const free_amount = body.free_amount ?? body.freeAmount ?? 0;
const donate_amount = body.donate_amount ?? body.donateAmount ?? 0;
const dust_amount = body.dust_amount ?? body.dustAmount ?? 0;
const db = getDb();
const player = db.prepare('SELECT * FROM players WHERE steam_id = ?').get(ctx.params.steamId) as any;
if (!player) throw new HttpError(404, 'Player not found');
db.prepare(`
UPDATE players SET free_currency = free_currency + ?, donate_currency = donate_currency + ?,
dust_currency = dust_currency + ?, updated_at = datetime('now') WHERE steam_id = ?
`).run(
free_amount,
donate_amount,
dust_amount,
ctx.params.steamId
);
return { success: true };
});
// POST /player/:steamId/purchases — Record a store purchase
route('player/:steamId/purchases', ['POST'], (ctx: HandlerContext) => {
const { item_id, item_category, card_id, price_free, price_donate, price_dust } = ctx.body as any;
const db = getDb();
db.prepare(`
INSERT INTO purchases (steam_id, item_id, item_category, card_id, price_free, price_donate, price_dust)
VALUES (?, ?, ?, ?, ?, ?, ?)
`).run(ctx.params.steamId, item_id, item_category || 'items', card_id || null, price_free || 0, price_donate || 0, price_dust || 0);
return { success: true };
});
// POST /player/:steamId/promo/redeem — Redeem a promo code
route('player/:steamId/promo/redeem', ['POST'], (ctx: HandlerContext) => {
const { code } = ctx.body as any;
if (!code) throw new HttpError(400, 'Code is required');
const normalizedCode = String(code).toUpperCase();
const db = getDb();
const promo = db.prepare('SELECT * FROM promo_codes WHERE code = ?').get(normalizedCode) as any;
if (!promo) throw new HttpError(404, 'Promo code not found');
if (promo.expires_at && new Date(promo.expires_at) < new Date()) throw new HttpError(400, 'Code expired');
if (promo.current_uses >= promo.max_uses) throw new HttpError(400, 'Code fully redeemed');
const existing = db.prepare('SELECT * FROM promo_redemptions WHERE steam_id = ? AND code = ?').get(ctx.params.steamId, normalizedCode);
if (existing) throw new HttpError(400, 'Code already redeemed');
db.prepare(`
UPDATE players SET free_currency = free_currency + ?, donate_currency = donate_currency + ?,
dust_currency = dust_currency + ?, updated_at = datetime('now') WHERE steam_id = ?
`).run(promo.free_currency, promo.donate_currency, promo.dust_currency, ctx.params.steamId);
db.prepare('UPDATE promo_codes SET current_uses = current_uses + 1 WHERE code = ?').run(normalizedCode);
db.prepare('INSERT INTO promo_redemptions (steam_id, code) VALUES (?, ?)').run(ctx.params.steamId, normalizedCode);
const player = db.prepare('SELECT free_currency, donate_currency, dust_currency FROM players WHERE steam_id = ?').get(ctx.params.steamId);
return { success: true, rewards: { free_currency: promo.free_currency, donate_currency: promo.donate_currency, dust_currency: promo.dust_currency }, currency: player };
});
// GET /player/:steamId/sounds_wheel — Get sounds wheel
route('player/:steamId/sounds_wheel', ['GET'], (ctx: HandlerContext) => {
const db = getDb();
const player = db.prepare('SELECT sounds_wheel FROM players WHERE steam_id = ?').get(ctx.params.steamId) as any;
if (!player) throw new HttpError(404, 'Player not found');
return { sounds_wheel: JSON.parse(player.sounds_wheel || '{}') };
});
// PUT /player/:steamId/sounds_wheel — Save sounds wheel
route('player/:steamId/sounds_wheel', ['PUT'], (ctx: HandlerContext) => {
const { sounds_wheel } = ctx.body as any;
const db = getDb();
db.prepare("UPDATE players SET sounds_wheel = ?, updated_at = datetime('now') WHERE steam_id = ?")
.run(JSON.stringify(sounds_wheel || {}), ctx.params.steamId);
return { success: true };
});
// POST /player/:steamId/deal-purchase — Buy a deal/offer
route('player/:steamId/deal-purchase', ['POST'], (ctx: HandlerContext) => {
const { deal_key } = ctx.body as any;
return { success: true, ok: true, item_id: 'deal_' + deal_key, item_category: 'items' };
});
// GET /player/:steamId/active_effects — Get active cosmetic effects
route('player/:steamId/active_effects', ['GET'], (ctx: HandlerContext) => {
const db = getDb();
const row = db.prepare('SELECT effects FROM active_effects WHERE steam_id = ?').get(ctx.params.steamId) as any;
return { active_effects: row ? JSON.parse(row.effects) : {} };
});
// PUT /player/:steamId/active_effects — Save active effects
route('player/:steamId/active_effects', ['PUT'], (ctx: HandlerContext) => {
const { active_effects } = ctx.body as any;
const db = getDb();
db.prepare(`
INSERT INTO active_effects (steam_id, effects, updated_at) VALUES (?, ?, datetime('now'))
ON CONFLICT(steam_id) DO UPDATE SET effects = ?, updated_at = datetime('now')
`).run(ctx.params.steamId, JSON.stringify(active_effects || {}), JSON.stringify(active_effects || {}));
return { success: true };
});