diff --git a/package.json b/package.json index 67709728..c3cd6b0b 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "ml-distance": "^4.0.1", "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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 25f73461..c460c2c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -164,6 +164,9 @@ importers: 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 diff --git a/server/node/server.cjs b/server/node/server.cjs index 3a46102f..af4d189e 100644 --- a/server/node/server.cjs +++ b/server/node/server.cjs @@ -11,6 +11,8 @@ 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'); let password = '' @@ -29,7 +31,7 @@ function isHex(str) { } app.get('/', async (req, res, next) => { - console.log("connected") + console.log("[Server] Connected") try { const mainIndex = await fs.readFile(path.join(process.cwd(), 'dist', 'index.html')) const root = htmlparser.parse(mainIndex) @@ -137,12 +139,117 @@ const reverseProxyFunc_get = async (req, res, next) => { } } +// Risu Realm Proxy +async function hubProxyHandler(req, res, next) { + try { + // Extract request path and query parameters + const pathAndQuery = req.originalUrl.replace(/^\/hub-proxy/, ''); + const externalURL = EXTERNAL_HUB_URL + 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; + } + + // 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, + }); + + 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; + } + }); + + // 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 + } + + } catch (error) { + // Fetch request itself failed or other exceptions + console.error("[Hub Proxy] Request failed:", error); + if (!res.headersSent) { + res.status(502).send({ error: 'Proxy failed to connect to or get response from the hub server.' }); + } 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.post('/proxy', reverseProxyFunc); app.post('/proxy2', reverseProxyFunc); + app.get('/api/password', async(req, res)=> { if(password === ''){ res.send({status: 'unset'}) @@ -317,36 +424,37 @@ async function getHttpsOptions() { return { key, cert }; } catch (error) { - console.error('SSL setup errors:', error.message); - console.log('Start the server with HTTP instead of HTTPS...'); + console.error('[Server] SSL setup errors:', error.message); + console.log('[Server] Start the server with HTTP instead of HTTPS...'); return null; } } async function startServer() { - const port = process.env.PORT || 6001; - const httpsOptions = await getHttpsOptions(); + try { + + const port = process.env.PORT || 6001; + const httpsOptions = await getHttpsOptions(); - if (httpsOptions) { - // HTTPS - https.createServer(httpsOptions, app).listen(port, () => { - console.log("HTTPS server is running."); - console.log("https://localhost:6001/"); - }); - - } else { - // HTTP - app.listen(port, () => { - console.log("HTTP server is running."); - console.log("http://localhost:6001/"); - }); + if (httpsOptions) { + // HTTPS + https.createServer(httpsOptions, app).listen(port, () => { + console.log("[Server] HTTPS server is running."); + console.log(`[Server] https://localhost:${port}/`); + }); + } else { + // HTTP + app.listen(port, () => { + console.log("[Server] HTTP server is running."); + console.log(`[Server] http://localhost:${port}/`); + }); + } + } catch (error) { + console.error('[Server] Failed to start server :', error); + process.exit(1); } } (async () => { - try { - await startServer(); - } catch (error) { - console.error('Fail to start server :', error); - } + await startServer(); })(); \ No newline at end of file diff --git a/src/ts/characterCards.ts b/src/ts/characterCards.ts index 7e74558e..6c3fb44e 100644 --- a/src/ts/characterCards.ts +++ b/src/ts/characterCards.ts @@ -18,7 +18,11 @@ import { exportModule, readModule, type RisuModule } from "./process/modules" import { readFile } from "@tauri-apps/plugin-fs" import { onOpenUrl } from '@tauri-apps/plugin-deep-link'; -export const hubURL = "https://sv.risuai.xyz" + +const EXTERNAL_HUB_URL = 'https://sv.risuai.xyz'; +export const hubURL = typeof window !== 'undefined' && (window as any).__NODE__ === true + ? '/hub-proxy' + : EXTERNAL_HUB_URL; export async function importCharacter() { try {