mirror of
https://git.sanhost.net/sanasol/hytale-f2p
synced 2026-02-26 09:21:48 -03:00
Add proxy client and route downloads through it
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
HF2P_SECRET_KEY=YOUR_KEY_HERE
|
||||||
|
HF2P_PROXY_URL=YOUR_PROXY
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { downloadFile, findHomePageUIPath, findLogoPath } = require('../utils/fileManager');
|
const { downloadFile, findHomePageUIPath, findLogoPath } = require('../utils/fileManager');
|
||||||
|
const { proxyRequest } = require('../utils/proxyClient');
|
||||||
|
|
||||||
async function downloadAndReplaceHomePageUI(gameDir, progressCallback) {
|
async function downloadAndReplaceHomePageUI(gameDir, progressCallback) {
|
||||||
try {
|
try {
|
||||||
@@ -13,7 +14,8 @@ async function downloadAndReplaceHomePageUI(gameDir, progressCallback) {
|
|||||||
const homeUIUrl = 'https://files.hytalef2p.com/api/HomeUI';
|
const homeUIUrl = 'https://files.hytalef2p.com/api/HomeUI';
|
||||||
const tempHomePath = path.join(path.dirname(gameDir), 'HomePage_temp.ui');
|
const tempHomePath = path.join(path.dirname(gameDir), 'HomePage_temp.ui');
|
||||||
|
|
||||||
await downloadFile(homeUIUrl, tempHomePath);
|
const response = await proxyRequest(homeUIUrl, { responseType: 'arraybuffer' });
|
||||||
|
fs.writeFileSync(tempHomePath, response.data);
|
||||||
|
|
||||||
const existingHomePath = findHomePageUIPath(gameDir);
|
const existingHomePath = findHomePageUIPath(gameDir);
|
||||||
|
|
||||||
@@ -66,7 +68,8 @@ async function downloadAndReplaceLogo(gameDir, progressCallback) {
|
|||||||
const logoUrl = 'https://files.hytalef2p.com/api/Logo';
|
const logoUrl = 'https://files.hytalef2p.com/api/Logo';
|
||||||
const tempLogoPath = path.join(path.dirname(gameDir), 'Logo@2x_temp.png');
|
const tempLogoPath = path.join(path.dirname(gameDir), 'Logo@2x_temp.png');
|
||||||
|
|
||||||
await downloadFile(logoUrl, tempLogoPath);
|
const response = await proxyRequest(logoUrl, { responseType: 'arraybuffer' });
|
||||||
|
fs.writeFileSync(tempLogoPath, response.data);
|
||||||
|
|
||||||
const existingLogoPath = findLogoPath(gameDir);
|
const existingLogoPath = findLogoPath(gameDir);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ const axios = require('axios');
|
|||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { getOS, getArch } = require('../utils/platformUtils');
|
const { getOS, getArch } = require('../utils/platformUtils');
|
||||||
|
const { proxyRequest } = require('../utils/proxyClient');
|
||||||
|
|
||||||
const BASE_PATCH_URL = 'https://game-patches.hytale.com/patches';
|
const BASE_PATCH_URL = 'https://game-patches.hytale.com/patches';
|
||||||
const MANIFEST_API = 'https://files.hytalef2p.com/api/patch_manifest';
|
const MANIFEST_API = 'https://files.hytalef2p.com/api/patch_manifest';
|
||||||
@@ -9,8 +10,7 @@ const MANIFEST_API = 'https://files.hytalef2p.com/api/patch_manifest';
|
|||||||
async function getLatestClientVersion(branch = 'release') {
|
async function getLatestClientVersion(branch = 'release') {
|
||||||
try {
|
try {
|
||||||
console.log(`Fetching latest client version from API (branch: ${branch})...`);
|
console.log(`Fetching latest client version from API (branch: ${branch})...`);
|
||||||
const response = await axios.get('https://files.hytalef2p.com/api/version_client', {
|
const response = await proxyRequest(`https://files.hytalef2p.com/api/version_client?branch=${branch}`, {
|
||||||
params: { branch },
|
|
||||||
timeout: 40000,
|
timeout: 40000,
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': 'Hytale-F2P-Launcher'
|
'User-Agent': 'Hytale-F2P-Launcher'
|
||||||
@@ -66,8 +66,7 @@ async function fetchPatchManifest(branch = 'release') {
|
|||||||
try {
|
try {
|
||||||
const os = getOS();
|
const os = getOS();
|
||||||
const arch = getArch();
|
const arch = getArch();
|
||||||
const response = await axios.get(MANIFEST_API, {
|
const response = await proxyRequest(`${MANIFEST_API}?branch=${branch}&os=${os}&arch=${arch}`, {
|
||||||
params: { branch, os, arch },
|
|
||||||
timeout: 10000
|
timeout: 10000
|
||||||
});
|
});
|
||||||
return response.data.patches || {};
|
return response.data.patches || {};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const { getProxyDownloadStream } = require('./proxyClient');
|
||||||
|
|
||||||
// Domain configuration
|
// Domain configuration
|
||||||
const ORIGINAL_DOMAIN = 'hytale.com';
|
const ORIGINAL_DOMAIN = 'hytale.com';
|
||||||
@@ -605,9 +606,6 @@ class ClientPatcher {
|
|||||||
console.log('Downloading pre-patched HytaleServer.jar...');
|
console.log('Downloading pre-patched HytaleServer.jar...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const https = require('https');
|
|
||||||
|
|
||||||
// Use different URL for pre-release vs release
|
|
||||||
let url;
|
let url;
|
||||||
if (branch === 'pre-release') {
|
if (branch === 'pre-release') {
|
||||||
url = 'https://patcher.authbp.xyz/download/patched_prerelease';
|
url = 'https://patcher.authbp.xyz/download/patched_prerelease';
|
||||||
@@ -617,41 +615,28 @@ class ClientPatcher {
|
|||||||
console.log(' Using release patched server from:', url);
|
console.log(' Using release patched server from:', url);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
const handleResponse = (response) => {
|
|
||||||
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
||||||
https.get(response.headers.location, handleResponse).on('error', reject);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.statusCode !== 200) {
|
|
||||||
reject(new Error(`Failed to download: HTTP ${response.statusCode}`));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const file = fs.createWriteStream(serverPath);
|
const file = fs.createWriteStream(serverPath);
|
||||||
const totalSize = parseInt(response.headers['content-length'], 10);
|
let totalSize = 0;
|
||||||
let downloaded = 0;
|
let downloaded = 0;
|
||||||
|
|
||||||
response.on('data', (chunk) => {
|
const stream = await getProxyDownloadStream(url, (chunk, downloadedBytes, total) => {
|
||||||
downloaded += chunk.length;
|
downloaded = downloadedBytes;
|
||||||
|
totalSize = total;
|
||||||
if (progressCallback && totalSize) {
|
if (progressCallback && totalSize) {
|
||||||
const percent = 30 + Math.floor((downloaded / totalSize) * 60);
|
const percent = 30 + Math.floor((downloaded / totalSize) * 60);
|
||||||
progressCallback(`Downloading... ${(downloaded / 1024 / 1024).toFixed(2)} MB`, percent);
|
progressCallback(`Downloading... ${(downloaded / 1024 / 1024).toFixed(2)} MB`, percent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
response.pipe(file);
|
stream.pipe(file);
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
file.on('finish', () => {
|
file.on('finish', () => {
|
||||||
file.close();
|
file.close();
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
};
|
file.on('error', reject);
|
||||||
|
stream.on('error', reject);
|
||||||
https.get(url, handleResponse).on('error', (err) => {
|
|
||||||
fs.unlink(serverPath, () => {});
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(' Download successful');
|
console.log(' Download successful');
|
||||||
|
|||||||
96
backend/utils/proxyClient.js
Normal file
96
backend/utils/proxyClient.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
const crypto = require('crypto');
|
||||||
|
const axios = require('axios');
|
||||||
|
const https = require('https');
|
||||||
|
|
||||||
|
const PROXY_URL = process.env.HF2P_PROXY_URL || 'your_proxy_url_here';
|
||||||
|
const SECRET_KEY = process.env.HF2P_SECRET_KEY || 'your_secret_key_here_for_jwt';
|
||||||
|
|
||||||
|
function generateToken() {
|
||||||
|
const timestamp = Date.now().toString();
|
||||||
|
const hash = crypto
|
||||||
|
.createHmac('sha256', SECRET_KEY)
|
||||||
|
.update(timestamp)
|
||||||
|
.digest('hex');
|
||||||
|
return `${timestamp}:${hash}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function proxyRequest(url, options = {}) {
|
||||||
|
const token = generateToken();
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
const targetUrl = `${urlObj.protocol}//${urlObj.host}`;
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
method: options.method || 'GET',
|
||||||
|
url: `${PROXY_URL}/proxy${urlObj.pathname}${urlObj.search}`,
|
||||||
|
headers: {
|
||||||
|
'X-Auth-Token': token,
|
||||||
|
'X-Target-URL': targetUrl,
|
||||||
|
...(options.headers || {})
|
||||||
|
},
|
||||||
|
timeout: options.timeout || 30000,
|
||||||
|
responseType: options.responseType
|
||||||
|
};
|
||||||
|
|
||||||
|
return axios(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProxyDownloadStream(url, onData) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const token = generateToken();
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
const targetUrl = `${urlObj.protocol}//${urlObj.host}`;
|
||||||
|
|
||||||
|
const proxyUrl = new URL(PROXY_URL);
|
||||||
|
const requestPath = `/proxy${urlObj.pathname}${urlObj.search}`;
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
hostname: proxyUrl.hostname,
|
||||||
|
port: proxyUrl.port || (proxyUrl.protocol === 'https:' ? 443 : 80),
|
||||||
|
path: requestPath,
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-Auth-Token': token,
|
||||||
|
'X-Target-URL': targetUrl
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const protocol = proxyUrl.protocol === 'https:' ? https : require('http');
|
||||||
|
|
||||||
|
const handleResponse = (response) => {
|
||||||
|
if (response.statusCode === 302 || response.statusCode === 301) {
|
||||||
|
const redirectUrl = response.headers.location;
|
||||||
|
if (redirectUrl.startsWith('http')) {
|
||||||
|
getProxyDownloadStream(redirectUrl, onData).then(resolve).catch(reject);
|
||||||
|
} else {
|
||||||
|
reject(new Error(`Invalid redirect: ${redirectUrl}`));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.statusCode !== 200) {
|
||||||
|
reject(new Error(`HTTP ${response.statusCode}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalSize = parseInt(response.headers['content-length'], 10);
|
||||||
|
let downloaded = 0;
|
||||||
|
|
||||||
|
response.on('data', (chunk) => {
|
||||||
|
downloaded += chunk.length;
|
||||||
|
if (onData) {
|
||||||
|
onData(chunk, downloaded, totalSize);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(response);
|
||||||
|
};
|
||||||
|
|
||||||
|
protocol.get(options, handleResponse).on('error', reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
proxyRequest,
|
||||||
|
getProxyDownloadStream,
|
||||||
|
generateToken
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user