Fix: Resolve account login issues in Node.js hosted version

In a previous commit, a new proxy endpoint was added to the backend to resolve CORS errors that occurred when the frontend directly fetched data from Risu Realm.

However, the proxy endpoint handler failed to properly process POST requests, causing account login failures. Additionally, the backend was inefficiently performing complex body processing.

The updated code now directly pipes the original request, providing a more efficient and reliable solution.
This commit is contained in:
shirosaki-hana
2025-04-17 13:14:12 +09:00
parent 21561fe5ff
commit e0038749a4
3 changed files with 45 additions and 100 deletions

View File

@@ -69,7 +69,6 @@
"mnemonist": "^0.40.3", "mnemonist": "^0.40.3",
"mobile-drag-drop": "3.0.0-rc.0", "mobile-drag-drop": "3.0.0-rc.0",
"msgpackr": "1.10.1", "msgpackr": "1.10.1",
"node-fetch": "2",
"node-html-parser": "^6.1.12", "node-html-parser": "^6.1.12",
"ollama": "^0.5.0", "ollama": "^0.5.0",
"pdfjs-dist": "^4.0.379", "pdfjs-dist": "^4.0.379",

18
pnpm-lock.yaml generated
View File

@@ -158,15 +158,15 @@ importers:
ml-distance: ml-distance:
specifier: ^4.0.1 specifier: ^4.0.1
version: 4.0.1 version: 4.0.1
mnemonist:
specifier: ^0.40.3
version: 0.40.3
mobile-drag-drop: mobile-drag-drop:
specifier: 3.0.0-rc.0 specifier: 3.0.0-rc.0
version: 3.0.0-rc.0 version: 3.0.0-rc.0
msgpackr: msgpackr:
specifier: 1.10.1 specifier: 1.10.1
version: 1.10.1 version: 1.10.1
node-fetch:
specifier: '2'
version: 2.7.0
node-html-parser: node-html-parser:
specifier: ^6.1.12 specifier: ^6.1.12
version: 6.1.12 version: 6.1.12
@@ -2756,6 +2756,9 @@ packages:
ml-tree-similarity@1.0.0: ml-tree-similarity@1.0.0:
resolution: {integrity: sha512-XJUyYqjSuUQkNQHMscr6tcjldsOoAekxADTplt40QKfwW6nd++1wHWV9AArl0Zvw/TIHgNaZZNvr8QGvE8wLRg==} resolution: {integrity: sha512-XJUyYqjSuUQkNQHMscr6tcjldsOoAekxADTplt40QKfwW6nd++1wHWV9AArl0Zvw/TIHgNaZZNvr8QGvE8wLRg==}
mnemonist@0.40.3:
resolution: {integrity: sha512-Vjyr90sJ23CKKH/qPAgUKicw/v6pRoamxIEDFOF8uSgFME7DqPRpHgRTejWVjkdGg5dXj0/NyxZHZ9bcjH+2uQ==}
mobile-drag-drop@3.0.0-rc.0: mobile-drag-drop@3.0.0-rc.0:
resolution: {integrity: sha512-f8wIDTbBYLBW/+5sei1cqUE+StyDpf/LP+FRZELlVX6tmOOmELk84r3wh1z3woxCB9G5octhF06K5COvFjGgqg==} resolution: {integrity: sha512-f8wIDTbBYLBW/+5sei1cqUE+StyDpf/LP+FRZELlVX6tmOOmELk84r3wh1z3woxCB9G5octhF06K5COvFjGgqg==}
@@ -2900,6 +2903,9 @@ packages:
object-inspect@1.13.1: object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
obliterator@2.0.5:
resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==}
ollama@0.5.0: ollama@0.5.0:
resolution: {integrity: sha512-CRtRzsho210EGdK52GrUMohA2pU+7NbgEaBG3DcYeRmvQthDO7E2LHOkLlUUeaYUlNmEd8icbjC02ug9meSYnw==} resolution: {integrity: sha512-CRtRzsho210EGdK52GrUMohA2pU+7NbgEaBG3DcYeRmvQthDO7E2LHOkLlUUeaYUlNmEd8icbjC02ug9meSYnw==}
@@ -6505,6 +6511,10 @@ snapshots:
binary-search: 1.3.6 binary-search: 1.3.6
num-sort: 2.1.0 num-sort: 2.1.0
mnemonist@0.40.3:
dependencies:
obliterator: 2.0.5
mobile-drag-drop@3.0.0-rc.0: {} mobile-drag-drop@3.0.0-rc.0: {}
modify-values@1.0.1: {} modify-values@1.0.1: {}
@@ -6665,6 +6675,8 @@ snapshots:
object-inspect@1.13.1: {} object-inspect@1.13.1: {}
obliterator@2.0.5: {}
ollama@0.5.0: ollama@0.5.0:
dependencies: dependencies:
whatwg-fetch: 3.6.20 whatwg-fetch: 3.6.20

View File

@@ -11,8 +11,7 @@ app.use(express.raw({ type: 'application/octet-stream', limit: '50mb' }));
const {pipeline} = require('stream/promises') const {pipeline} = require('stream/promises')
const https = require('https'); const https = require('https');
const sslPath = path.join(process.cwd(), 'server/node/ssl/certificate'); const sslPath = path.join(process.cwd(), 'server/node/ssl/certificate');
const EXTERNAL_HUB_URL = 'https://sv.risuai.xyz'; const hubURL = 'https://sv.risuai.xyz';
const fetch = require('node-fetch');
let password = '' let password = ''
@@ -31,12 +30,17 @@ function isHex(str) {
} }
app.get('/', async (req, res, next) => { app.get('/', async (req, res, next) => {
console.log("[Server] Connected")
const clientIP = req.headers['x-forwarded-for'] || req.ip || req.socket.remoteAddress || 'Unknown IP';
const timestamp = new Date().toISOString();
console.log(`[Server] ${timestamp} | Connection from: ${clientIP}`);
try { try {
const mainIndex = await fs.readFile(path.join(process.cwd(), 'dist', 'index.html')) const mainIndex = await fs.readFile(path.join(process.cwd(), 'dist', 'index.html'))
const root = htmlparser.parse(mainIndex) const root = htmlparser.parse(mainIndex)
const head = root.querySelector('head') const head = root.querySelector('head')
head.innerHTML = `<script>globalThis.__NODE__ = true</script>` + head.innerHTML head.innerHTML = `<script>globalThis.__NODE__ = true</script>` + head.innerHTML
res.send(root.toString()) res.send(root.toString())
} catch (error) { } catch (error) {
console.log(error) console.log(error)
@@ -139,116 +143,49 @@ const reverseProxyFunc_get = async (req, res, next) => {
} }
} }
// Risu Realm Proxy async function hubProxyFunc(req, res) {
async function hubProxyHandler(req, res, next) {
try { try {
// Extract request path and query parameters
const pathAndQuery = req.originalUrl.replace(/^\/hub-proxy/, ''); const pathAndQuery = req.originalUrl.replace(/^\/hub-proxy/, '');
const externalURL = EXTERNAL_HUB_URL + pathAndQuery; const externalURL = hubURL + pathAndQuery;
console.log(`[Hub Proxy] Forwarding ${req.method} request to: ${externalURL}`);
// Prepare headers to send to the realm server (including Accept-Encoding modification)
const headersToSend = { ...req.headers }; const headersToSend = { ...req.headers };
delete headersToSend['host']; delete headersToSend.host;
delete headersToSend['connection']; delete headersToSend.connection;
headersToSend['accept-encoding'] = 'gzip, deflate'; // Exclude zstd, etc.
if (!headersToSend['x-forwarded-for']) { const proxyReq = new Request(externalURL, {
headersToSend['x-forwarded-for'] = req.ip;
}
// Execute the fetch request to the realm server
const response = await fetch(externalURL, {
method: req.method, method: req.method,
headers: headersToSend, headers: headersToSend,
body: (req.method !== 'GET' && req.method !== 'HEAD') ? req.body : undefined, body: req.method !== 'GET' && req.method !== 'HEAD' ? req : undefined,
duplex: 'half'
}); });
console.log(`[Hub Proxy] Received status ${response.status} from external server`); const response = await fetch(proxyReq);
// Handle the realm server response for (const [key, value] of response.headers.entries()) {
// Clean up response headers and extract Content-Type res.setHeader(key, value);
const responseHeaders = {};
// Check the Content-Type of the realm server response (use default if missing)
let contentType = response.headers.get('content-type') || 'application/octet-stream';
response.headers.forEach((value, key) => {
const lowerKey = key.toLowerCase();
// List of headers not to be forwarded to the client
const excludedHeaders = [
'transfer-encoding', 'connection', 'content-encoding',
'access-control-allow-origin', 'access-control-allow-methods',
'access-control-allow-headers', 'content-security-policy',
'content-security-policy-report-only', 'clear-site-data',
'strict-transport-security', 'expect-ct',
'cf-ray', 'cf-cache-status', 'report-to', 'nel', 'server', 'server-timing', 'alt-svc'
];
if (!excludedHeaders.includes(lowerKey)) {
responseHeaders[key] = value;
}
});
// Set the status code and cleaned headers for the client
res.status(response.status).set(responseHeaders);
// Determine body processing method based on Content-Type
try {
if (contentType.startsWith('application/json')) {
// JSON response: read as text and send
const bodyText = await response.text();
console.log(`[Hub Proxy] Processing JSON response (size: ${bodyText.length})`);
res.setHeader('Content-Type', contentType); // Set the final Content-Type
res.send(bodyText);
} else if (contentType.startsWith('image/')) {
// Image response: read as buffer and send
const bodyBuffer = await response.buffer(); // Assuming 'fetch' response object has a .buffer() method or similar
console.log(`[Hub Proxy] Processing Image response (type: ${contentType}, size: ${bodyBuffer.length} bytes)`);
res.setHeader('Content-Type', contentType); // Set the final Content-Type
res.send(bodyBuffer);
} else {
// Other responses (HTML, other text, unknown binary, etc.): read as buffer and send safely
const bodyBuffer = await response.buffer(); // Assuming 'fetch' response object has a .buffer() method or similar
console.log(`[Hub Proxy] Processing Other response as buffer (type: ${contentType}, size: ${bodyBuffer.length} bytes)`);
// Use original Content-Type if available, otherwise use octet-stream (already handled by default assignment)
res.setHeader('Content-Type', contentType);
res.send(bodyBuffer);
}
} catch (bodyError) {
// If an error occurs while reading/processing the response body
console.error("[Hub Proxy] Error reading/processing response body:", bodyError);
if (!res.headersSent) {
res.status(500).send({ error: 'Failed to process response body from hub server.' });
} else {
console.error("[Hub Proxy] Headers already sent, cannot send body error to client.");
res.end();
}
return; // End the handler
} }
res.status(response.status);
await pipeline(response.body, res);
} catch (error) { } catch (error) {
// Fetch request itself failed or other exceptions console.error("[Hub Proxy] Error:", error);
console.error("[Hub Proxy] Request failed:", error);
if (!res.headersSent) { if (!res.headersSent) {
res.status(502).send({ error: 'Proxy failed to connect to or get response from the hub server.' }); res.status(502).send({ error: 'Proxy request failed' });
} else { } else {
console.error("[Hub Proxy] Headers already sent, cannot send connection error to client.");
res.end(); res.end();
} }
} }
} }
app.get('/hub-proxy/*', hubProxyHandler);
app.post('/hub-proxy/*', hubProxyHandler);
app.put('/hub-proxy/*', hubProxyHandler);
app.get('/proxy', reverseProxyFunc_get); app.get('/proxy', reverseProxyFunc_get);
app.get('/proxy2', reverseProxyFunc_get); app.get('/proxy2', reverseProxyFunc_get);
app.get('/hub-proxy/*', hubProxyFunc);
app.post('/proxy', reverseProxyFunc); app.post('/proxy', reverseProxyFunc);
app.post('/proxy2', reverseProxyFunc); app.post('/proxy2', reverseProxyFunc);
app.post('/hub-proxy/*', hubProxyFunc);
app.get('/api/password', async(req, res)=> { app.get('/api/password', async(req, res)=> {
if(password === ''){ if(password === ''){
@@ -408,9 +345,6 @@ async function getHttpsOptions() {
const keyPath = path.join(sslPath, 'server.key'); const keyPath = path.join(sslPath, 'server.key');
const certPath = path.join(sslPath, 'server.crt'); const certPath = path.join(sslPath, 'server.crt');
console.log(keyPath)
console.log(certPath)
try { try {
await fs.access(keyPath); await fs.access(keyPath);