const express = require('express'); const app = express(); const path = require('path'); const htmlparser = require('node-html-parser'); const { existsSync, mkdirSync, readFileSync, writeFileSync } = require('fs'); const bodyParser = require('body-parser'); const fs = require('fs/promises') const crypto = require('crypto') app.use(express.static(path.join(process.cwd(), 'dist'), {index: false})); app.use(bodyParser.raw({ limit: 100000000 })); app.use(bodyParser.json()) const {pipeline} = require('stream/promises') let password = '' const savePath = path.join(process.cwd(), "save") if(!existsSync(savePath)){ mkdirSync(savePath) } const passwordPath = path.join(process.cwd(), 'save', '__password') if(existsSync(passwordPath)){ password = readFileSync(passwordPath, 'utf-8') } const hexRegex = /^[0-9a-fA-F]+$/; function isHex(str) { return hexRegex.test(str.toUpperCase().trim()) || str === '__password'; } app.get('/', async (req, res, next) => { console.log("connected") 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 = `` + head.innerHTML res.send(root.toString()) } catch (error) { console.log(error) next(error) } }) const reverseProxyFunc = async (req, res, next) => { const urlParam = req.headers['risu-url'] ? decodeURIComponent(req.headers['risu-url']) : req.query.url; if (!urlParam) { res.status(400).send({ error:'URL has no param' }); return; } const header = req.headers['risu-header'] ? JSON.parse(decodeURIComponent(req.headers['risu-header'])) : req.headers; if(!header['x-forwarded-for']){ header['x-forwarded-for'] = req.ip } let originalResponse; try { // make request to original server originalResponse = await fetch(urlParam, { method: req.method, headers: header, body: JSON.stringify(req.body) }); // get response body as stream const originalBody = originalResponse.body; // get response headers const head = new Headers(originalResponse.headers); head.delete('content-security-policy'); head.delete('content-security-policy-report-only'); head.delete('clear-site-data'); head.delete('Cache-Control'); head.delete('Content-Encoding'); const headObj = {}; for (let [k, v] of head) { headObj[k] = v; } // send response headers to client res.header(headObj); // send response status to client res.status(originalResponse.status); // send response body to client await pipeline(originalResponse.body, res); } catch (err) { next(err); return; } } app.post('/proxy', reverseProxyFunc); app.post('/proxy2', reverseProxyFunc); app.get('/api/password', async(req, res)=> { if(password === ''){ res.send({status: 'unset'}) } else if(req.headers['risu-auth'] === password){ res.send({status:'correct'}) } else{ res.send({status:'incorrect'}) } }) app.post('/api/crypto', async (req, res) => { try { const hash = crypto.createHash('sha256') hash.update(Buffer.from(req.body.data, 'utf-8')) res.send(hash.digest('hex')) } catch (error) { next(error) } }) app.post('/api/set_password', async (req, res) => { if(password === ''){ password = req.body.password writeFileSync(passwordPath, password, 'utf-8') } res.status(400).send("already set") }) app.get('/api/read', async (req, res, next) => { if(req.headers['risu-auth'].trim() !== password.trim()){ console.log('incorrect') res.status(400).send({ error:'Password Incorrect' }); return } const filePath = req.headers['file-path']; if (!filePath) { console.log('no path') res.status(400).send({ error:'File path required' }); return; } if(!isHex(filePath)){ res.status(400).send({ error:'Invaild Path' }); return; } try { if(!existsSync(path.join(savePath, filePath))){ res.send(); } else{ res.setHeader('Content-Type','application/octet-stream'); res.sendFile(path.join(savePath, filePath)); } } catch (error) { next(error); } }); app.get('/api/remove', async (req, res, next) => { if(req.headers['risu-auth'].trim() !== password.trim()){ console.log('incorrect') res.status(400).send({ error:'Password Incorrect' }); return } const filePath = req.headers['file-path']; if (!filePath) { res.status(400).send({ error:'File path required' }); return; } if(!isHex(filePath)){ res.status(400).send({ error:'Invaild Path' }); return; } try { await fs.rm(path.join(savePath, filePath)); res.send({ success: true, }); } catch (error) { next(error); } }); app.get('/api/list', async (req, res, next) => { if(req.headers['risu-auth'].trim() !== password.trim()){ console.log('incorrect') res.status(400).send({ error:'Password Incorrect' }); return } try { const data = (await fs.readdir(path.join(savePath))).map((v) => { return Buffer.from(v, 'hex').toString('utf-8') }) res.send({ success: true, content: data }); } catch (error) { next(error); } }); app.post('/api/write', async (req, res, next) => { if(req.headers['risu-auth'].trim() !== password.trim()){ console.log('incorrect') res.status(400).send({ error:'Password Incorrect' }); return } const filePath = req.headers['file-path']; const fileContent = req.body if (!filePath || !fileContent) { res.status(400).send({ error:'File path required' }); return; } if(!isHex(filePath)){ res.status(400).send({ error:'Invaild Path' }); return; } try { await fs.writeFile(path.join(savePath, filePath), fileContent); res.send({ success: true }); } catch (error) { next(error); } }); app.listen(6001, () => { console.log("Server is listening on http://localhost:6001/"); });