158 lines
4.3 KiB
JavaScript
158 lines
4.3 KiB
JavaScript
/**
|
|
* GOG Backend - Unofficial GOG API Integration
|
|
* Uses Galaxy client credentials (well-known, used by lgogdownloader etc.)
|
|
*/
|
|
|
|
const CLIENT_ID = "46899977096215655";
|
|
const CLIENT_SECRET =
|
|
"9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9";
|
|
const REDIRECT_URI =
|
|
"https://embed.gog.com/on_login_success?origin=client";
|
|
|
|
/**
|
|
* Exchange authorization code for access + refresh tokens
|
|
* @param {string} code - Auth code from GOG login redirect
|
|
* @returns {Promise<{access_token: string, refresh_token: string, user_id: string, expires_in: number}>}
|
|
*/
|
|
export async function exchangeGogCode(code) {
|
|
if (!code) {
|
|
throw new Error("Authorization code ist erforderlich");
|
|
}
|
|
|
|
const url = new URL("https://auth.gog.com/token");
|
|
url.searchParams.set("client_id", CLIENT_ID);
|
|
url.searchParams.set("client_secret", CLIENT_SECRET);
|
|
url.searchParams.set("grant_type", "authorization_code");
|
|
url.searchParams.set("code", code);
|
|
url.searchParams.set("redirect_uri", REDIRECT_URI);
|
|
|
|
const response = await fetch(url);
|
|
|
|
if (!response.ok) {
|
|
const text = await response.text();
|
|
throw new Error(
|
|
`GOG Token Exchange Error: ${response.status} ${text}`,
|
|
);
|
|
}
|
|
|
|
const data = await response.json();
|
|
return {
|
|
access_token: data.access_token,
|
|
refresh_token: data.refresh_token,
|
|
user_id: data.user_id,
|
|
expires_in: data.expires_in,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Refresh an expired access token
|
|
* @param {string} refreshToken
|
|
* @returns {Promise<{access_token: string, refresh_token: string, expires_in: number}>}
|
|
*/
|
|
async function refreshAccessToken(refreshToken) {
|
|
const url = new URL("https://auth.gog.com/token");
|
|
url.searchParams.set("client_id", CLIENT_ID);
|
|
url.searchParams.set("client_secret", CLIENT_SECRET);
|
|
url.searchParams.set("grant_type", "refresh_token");
|
|
url.searchParams.set("refresh_token", refreshToken);
|
|
|
|
const response = await fetch(url);
|
|
|
|
if (!response.ok) {
|
|
const text = await response.text();
|
|
throw new Error(`GOG Token Refresh Error: ${response.status} ${text}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
return {
|
|
access_token: data.access_token,
|
|
refresh_token: data.refresh_token,
|
|
expires_in: data.expires_in,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Fetch all owned games from GOG
|
|
* @param {string} accessToken
|
|
* @param {string} refreshToken
|
|
* @returns {Promise<{games: Array, count: number, newAccessToken?: string, newRefreshToken?: string}>}
|
|
*/
|
|
export async function fetchGogGames(accessToken, refreshToken) {
|
|
if (!accessToken || !refreshToken) {
|
|
throw new Error("accessToken und refreshToken sind erforderlich");
|
|
}
|
|
|
|
let token = accessToken;
|
|
let newTokens = null;
|
|
|
|
// Fetch first page to get totalPages
|
|
let page = 1;
|
|
let totalPages = 1;
|
|
const allProducts = [];
|
|
|
|
while (page <= totalPages) {
|
|
const url = `https://embed.gog.com/account/getFilteredProducts?mediaType=1&page=${page}`;
|
|
|
|
let response = await fetch(url, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
|
|
// Token expired — try refresh
|
|
if (response.status === 401 && !newTokens) {
|
|
console.log("[GOG] Token expired, refreshing...");
|
|
newTokens = await refreshAccessToken(refreshToken);
|
|
token = newTokens.access_token;
|
|
|
|
response = await fetch(url, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
}
|
|
|
|
if (!response.ok) {
|
|
throw new Error(
|
|
`GOG API Error: ${response.status} ${response.statusText}`,
|
|
);
|
|
}
|
|
|
|
const data = await response.json();
|
|
totalPages = data.totalPages || 1;
|
|
allProducts.push(...(data.products || []));
|
|
page++;
|
|
}
|
|
|
|
// Transform to our Game format, skip products without title
|
|
const games = allProducts
|
|
.filter((product) => product.title)
|
|
.map((product) => ({
|
|
id: `gog-${product.id}`,
|
|
title: product.title,
|
|
source: "gog",
|
|
sourceId: String(product.id),
|
|
platform: "PC",
|
|
url: product.url
|
|
? `https://www.gog.com${product.url}`
|
|
: undefined,
|
|
}));
|
|
|
|
return {
|
|
games,
|
|
count: games.length,
|
|
...(newTokens && {
|
|
newAccessToken: newTokens.access_token,
|
|
newRefreshToken: newTokens.refresh_token,
|
|
}),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Returns the GOG auth URL for the user to open in their browser
|
|
*/
|
|
export function getGogAuthUrl() {
|
|
const url = new URL("https://auth.gog.com/auth");
|
|
url.searchParams.set("client_id", CLIENT_ID);
|
|
url.searchParams.set("redirect_uri", REDIRECT_URI);
|
|
url.searchParams.set("response_type", "code");
|
|
url.searchParams.set("layout", "client2");
|
|
return url.toString();
|
|
}
|