176 lines
4.5 KiB
JavaScript
176 lines
4.5 KiB
JavaScript
import express from "express";
|
|
import cors from "cors";
|
|
import fetch from "node-fetch";
|
|
import { exchangeGogCode, fetchGogGames } from "./gog-backend.mjs";
|
|
import { enrichGamesWithIgdb, loadCache } from "./igdb-cache.mjs";
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
// Enable CORS for your PWA
|
|
app.use(
|
|
cors({
|
|
origin: process.env.ALLOWED_ORIGIN || "*",
|
|
}),
|
|
);
|
|
|
|
app.use(express.json());
|
|
|
|
// Load IGDB cache on startup
|
|
loadCache();
|
|
|
|
// Health check
|
|
app.get("/health", (req, res) => {
|
|
res.json({ status: "ok" });
|
|
});
|
|
|
|
// Steam API refresh endpoint
|
|
app.post("/steam/refresh", async (req, res) => {
|
|
const { apiKey, steamId } = req.body;
|
|
|
|
console.log(`[Steam] Starting refresh for user: ${steamId}`);
|
|
|
|
if (!apiKey || !steamId) {
|
|
console.log("[Steam] Missing credentials");
|
|
return res.status(400).json({
|
|
error: "Missing required fields: apiKey and steamId",
|
|
});
|
|
}
|
|
|
|
try {
|
|
// Call Steam Web API
|
|
const steamUrl = `https://api.steampowered.com/IPlayerService/GetOwnedGames/v1/?key=${apiKey}&steamid=${steamId}&include_appinfo=1&include_played_free_games=1&format=json`;
|
|
|
|
console.log("[Steam] Calling Steam API...");
|
|
const response = await fetch(steamUrl);
|
|
console.log(`[Steam] Got response: ${response.status}`);
|
|
|
|
if (!response.ok) {
|
|
console.log(`[Steam] Steam API error: ${response.statusText}`);
|
|
return res.status(response.status).json({
|
|
error: "Steam API error",
|
|
message: response.statusText,
|
|
});
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log(`[Steam] Success! Games count: ${data.response?.game_count || 0}`);
|
|
|
|
const rawGames = data.response?.games || [];
|
|
|
|
// Enrich with IGDB canonical IDs
|
|
const gamesForIgdb = rawGames.map((g) => ({
|
|
...g,
|
|
source: "steam",
|
|
sourceId: String(g.appid),
|
|
}));
|
|
const enriched = await enrichGamesWithIgdb(gamesForIgdb);
|
|
|
|
// Return enriched games (source/sourceId/canonicalId included)
|
|
const transformed = {
|
|
games: enriched,
|
|
count: enriched.length,
|
|
};
|
|
|
|
const responseSize = JSON.stringify(transformed).length;
|
|
console.log(`[Steam] Sending response: ${responseSize} bytes, ${transformed.games.length} games`);
|
|
res.json(transformed);
|
|
console.log(`[Steam] Response sent successfully`);
|
|
} catch (error) {
|
|
console.error("[Steam] Exception:", error);
|
|
res.status(500).json({
|
|
error: "Failed to fetch games",
|
|
message: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
// GOG API: Exchange auth code for tokens
|
|
app.post("/gog/auth", async (req, res) => {
|
|
const { code } = req.body;
|
|
|
|
console.log("[GOG] Starting code exchange");
|
|
|
|
if (!code) {
|
|
return res.status(400).json({ error: "Missing required field: code" });
|
|
}
|
|
|
|
try {
|
|
const tokens = await exchangeGogCode(code);
|
|
console.log(`[GOG] Token exchange successful, user: ${tokens.user_id}`);
|
|
res.json(tokens);
|
|
} catch (error) {
|
|
console.error("[GOG] Token exchange error:", error);
|
|
res.status(500).json({
|
|
error: "GOG token exchange failed",
|
|
message: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
// GOG API: Refresh games
|
|
app.post("/gog/refresh", async (req, res) => {
|
|
const { accessToken, refreshToken } = req.body;
|
|
|
|
console.log("[GOG] Starting game refresh");
|
|
|
|
if (!accessToken || !refreshToken) {
|
|
return res.status(400).json({
|
|
error: "Missing required fields: accessToken and refreshToken",
|
|
});
|
|
}
|
|
|
|
try {
|
|
const result = await fetchGogGames(accessToken, refreshToken);
|
|
result.games = await enrichGamesWithIgdb(result.games);
|
|
console.log(`[GOG] Success! ${result.count} games fetched`);
|
|
res.json(result);
|
|
} catch (error) {
|
|
console.error("[GOG] Refresh error:", error);
|
|
res.status(500).json({
|
|
error: "GOG refresh failed",
|
|
message: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
// Fallback proxy for other Steam API calls
|
|
app.all("/*", async (req, res) => {
|
|
const path = req.url;
|
|
const steamUrl = `https://store.steampowered.com${path}`;
|
|
|
|
console.log(`Proxying: ${req.method} ${steamUrl}`);
|
|
|
|
try {
|
|
const response = await fetch(steamUrl, {
|
|
method: req.method,
|
|
headers: {
|
|
"User-Agent": "WhatToPlay/1.0",
|
|
Accept: "application/json",
|
|
...(req.body && { "Content-Type": "application/json" }),
|
|
},
|
|
...(req.body && { body: JSON.stringify(req.body) }),
|
|
});
|
|
|
|
const contentType = response.headers.get("content-type");
|
|
|
|
if (contentType && contentType.includes("application/json")) {
|
|
const data = await response.json();
|
|
res.json(data);
|
|
} else {
|
|
const text = await response.text();
|
|
res.send(text);
|
|
}
|
|
} catch (error) {
|
|
console.error("Proxy error:", error);
|
|
res.status(500).json({
|
|
error: "Proxy error",
|
|
message: error.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
app.listen(PORT, () => {
|
|
console.log(`Server running on port ${PORT}`);
|
|
});
|