Fix: Resolve account login issues in Node.js hosted version (#820)
# PR Checklist - ❌ Have you checked if it works normally in all models? *Ignore this if it doesn't use models.* - Not applicable - ✅ Have you checked if it works normally in all web, local, and node hosted versions? If it doesn't, have you blocked it in those versions? - Done - ❌ Have you added type definitions? - Not applicable # Description In the 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, 403 errors were occurring because the POST requests sent from the loadRisuAccountData() function in the frontend were not being properly processed. Additionally, the backend was inefficiently handling complex body processing. The updated backend code now provides a more efficient and stable solution by directly piping the original request. Furthermore, an issue where UserSettings.svelte couldn't handle tokens received through the backend proxy after successfully logging into a Risu account has also been fixed.
This commit is contained in:
@@ -69,7 +69,6 @@
|
||||
"mnemonist": "^0.40.3",
|
||||
"mobile-drag-drop": "3.0.0-rc.0",
|
||||
"msgpackr": "1.10.1",
|
||||
"node-fetch": "2",
|
||||
"node-html-parser": "^6.1.12",
|
||||
"ollama": "^0.5.0",
|
||||
"pdfjs-dist": "^4.0.379",
|
||||
|
||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@@ -158,15 +158,15 @@ importers:
|
||||
ml-distance:
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1
|
||||
mnemonist:
|
||||
specifier: ^0.40.3
|
||||
version: 0.40.3
|
||||
mobile-drag-drop:
|
||||
specifier: 3.0.0-rc.0
|
||||
version: 3.0.0-rc.0
|
||||
msgpackr:
|
||||
specifier: 1.10.1
|
||||
version: 1.10.1
|
||||
node-fetch:
|
||||
specifier: '2'
|
||||
version: 2.7.0
|
||||
node-html-parser:
|
||||
specifier: ^6.1.12
|
||||
version: 6.1.12
|
||||
@@ -2756,6 +2756,9 @@ packages:
|
||||
ml-tree-similarity@1.0.0:
|
||||
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:
|
||||
resolution: {integrity: sha512-f8wIDTbBYLBW/+5sei1cqUE+StyDpf/LP+FRZELlVX6tmOOmELk84r3wh1z3woxCB9G5octhF06K5COvFjGgqg==}
|
||||
|
||||
@@ -2900,6 +2903,9 @@ packages:
|
||||
object-inspect@1.13.1:
|
||||
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
|
||||
|
||||
obliterator@2.0.5:
|
||||
resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==}
|
||||
|
||||
ollama@0.5.0:
|
||||
resolution: {integrity: sha512-CRtRzsho210EGdK52GrUMohA2pU+7NbgEaBG3DcYeRmvQthDO7E2LHOkLlUUeaYUlNmEd8icbjC02ug9meSYnw==}
|
||||
|
||||
@@ -6505,6 +6511,10 @@ snapshots:
|
||||
binary-search: 1.3.6
|
||||
num-sort: 2.1.0
|
||||
|
||||
mnemonist@0.40.3:
|
||||
dependencies:
|
||||
obliterator: 2.0.5
|
||||
|
||||
mobile-drag-drop@3.0.0-rc.0: {}
|
||||
|
||||
modify-values@1.0.1: {}
|
||||
@@ -6665,6 +6675,8 @@ snapshots:
|
||||
|
||||
object-inspect@1.13.1: {}
|
||||
|
||||
obliterator@2.0.5: {}
|
||||
|
||||
ollama@0.5.0:
|
||||
dependencies:
|
||||
whatwg-fetch: 3.6.20
|
||||
|
||||
@@ -11,8 +11,7 @@ app.use(express.raw({ type: 'application/octet-stream', limit: '50mb' }));
|
||||
const {pipeline} = require('stream/promises')
|
||||
const https = require('https');
|
||||
const sslPath = path.join(process.cwd(), 'server/node/ssl/certificate');
|
||||
const EXTERNAL_HUB_URL = 'https://sv.risuai.xyz';
|
||||
const fetch = require('node-fetch');
|
||||
const hubURL = 'https://sv.risuai.xyz';
|
||||
|
||||
let password = ''
|
||||
|
||||
@@ -31,12 +30,17 @@ function isHex(str) {
|
||||
}
|
||||
|
||||
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 {
|
||||
const mainIndex = await fs.readFile(path.join(process.cwd(), 'dist', 'index.html'))
|
||||
const root = htmlparser.parse(mainIndex)
|
||||
const head = root.querySelector('head')
|
||||
head.innerHTML = `<script>globalThis.__NODE__ = true</script>` + head.innerHTML
|
||||
|
||||
res.send(root.toString())
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
@@ -139,116 +143,69 @@ const reverseProxyFunc_get = async (req, res, next) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Risu Realm Proxy
|
||||
async function hubProxyHandler(req, res, next) {
|
||||
async function hubProxyFunc(req, res) {
|
||||
|
||||
try {
|
||||
// Extract request path and query parameters
|
||||
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 };
|
||||
delete headersToSend['host'];
|
||||
delete headersToSend['connection'];
|
||||
headersToSend['accept-encoding'] = 'gzip, deflate'; // Exclude zstd, etc.
|
||||
if (!headersToSend['x-forwarded-for']) {
|
||||
headersToSend['x-forwarded-for'] = req.ip;
|
||||
}
|
||||
delete headersToSend.host;
|
||||
delete headersToSend.connection;
|
||||
|
||||
// Execute the fetch request to the realm server
|
||||
const response = await fetch(externalURL, {
|
||||
method: req.method,
|
||||
headers: headersToSend,
|
||||
body: (req.method !== 'GET' && req.method !== 'HEAD') ? req.body : undefined,
|
||||
body: req.method !== 'GET' && req.method !== 'HEAD' ? req : undefined,
|
||||
redirect: 'manual',
|
||||
duplex: 'half'
|
||||
});
|
||||
|
||||
console.log(`[Hub Proxy] Received status ${response.status} from external server`);
|
||||
|
||||
// Handle the realm server response
|
||||
// Clean up response headers and extract Content-Type
|
||||
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;
|
||||
for (const [key, value] of response.headers.entries()) {
|
||||
res.setHeader(key, value);
|
||||
}
|
||||
});
|
||||
res.status(response.status);
|
||||
|
||||
// Set the status code and cleaned headers for the client
|
||||
res.status(response.status).set(responseHeaders);
|
||||
if (response.status >= 300 && response.status < 400) {
|
||||
// Redirect handling (due to ‘/redirect/docs/lua’)
|
||||
const redirectUrl = response.headers.get('location');
|
||||
if (redirectUrl) {
|
||||
|
||||
// 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);
|
||||
if (redirectUrl.startsWith('http')) {
|
||||
|
||||
} 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);
|
||||
if (redirectUrl.startsWith(hubURL)) {
|
||||
const newPath = redirectUrl.replace(hubURL, '/hub-proxy');
|
||||
res.setHeader('location', newPath);
|
||||
}
|
||||
} 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();
|
||||
|
||||
} else if (redirectUrl.startsWith('/')) {
|
||||
|
||||
res.setHeader('location', `/hub-proxy${redirectUrl}`);
|
||||
}
|
||||
return; // End the handler
|
||||
}
|
||||
|
||||
return res.end();
|
||||
}
|
||||
|
||||
await pipeline(response.body, res);
|
||||
|
||||
} catch (error) {
|
||||
// Fetch request itself failed or other exceptions
|
||||
console.error("[Hub Proxy] Request failed:", error);
|
||||
console.error("[Hub Proxy] Error:", error);
|
||||
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: ' + error.message });
|
||||
} else {
|
||||
console.error("[Hub Proxy] Headers already sent, cannot send connection error to client.");
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/hub-proxy/*', hubProxyHandler);
|
||||
app.post('/hub-proxy/*', hubProxyHandler);
|
||||
app.put('/hub-proxy/*', hubProxyHandler);
|
||||
|
||||
app.get('/proxy', reverseProxyFunc_get);
|
||||
app.get('/proxy2', reverseProxyFunc_get);
|
||||
app.get('/hub-proxy/*', hubProxyFunc);
|
||||
|
||||
app.post('/proxy', reverseProxyFunc);
|
||||
app.post('/proxy2', reverseProxyFunc);
|
||||
|
||||
app.post('/hub-proxy/*', hubProxyFunc);
|
||||
|
||||
app.get('/api/password', async(req, res)=> {
|
||||
if(password === ''){
|
||||
@@ -408,9 +365,6 @@ async function getHttpsOptions() {
|
||||
const keyPath = path.join(sslPath, 'server.key');
|
||||
const certPath = path.join(sslPath, 'server.crt');
|
||||
|
||||
console.log(keyPath)
|
||||
console.log(certPath)
|
||||
|
||||
try {
|
||||
|
||||
await fs.access(keyPath);
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
</script>
|
||||
|
||||
<svelte:window onmessage={async (e) => {
|
||||
if(e.origin.startsWith("https://sv.risuai.xyz") || e.origin.startsWith("http://127.0.0.1")){
|
||||
if(e.origin.startsWith("https://sv.risuai.xyz") || e.origin.startsWith("http://127.0.0.1") || e.origin === window.location.origin){
|
||||
if(e.data.msg.data.vaild && $alertStore.type === 'login'){
|
||||
$alertStore = {
|
||||
type: 'none',
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</script>
|
||||
|
||||
<svelte:window onmessage={async (e) => {
|
||||
if(e.origin.startsWith("https://sv.risuai.xyz") || e.origin.startsWith("http://127.0.0.1")){
|
||||
if(e.origin.startsWith("https://sv.risuai.xyz") || e.origin.startsWith("http://127.0.0.1") || e.origin === window.location.origin){
|
||||
if(e.data.msg.type === 'drive'){
|
||||
await loadRisuAccountData()
|
||||
DBState.db.account.data.refresh_token = e.data.msg.data.refresh_token
|
||||
|
||||
Reference in New Issue
Block a user