Spaces:
Sleeping
Sleeping
| const http = require('node:http'); | |
| const { Buffer } = require('node:buffer'); | |
| const port = Number(process.env.PORT || 7860); | |
| const upstreamBase = process.env.UPSTREAM_BASE_URL || 'https://moonlantern1-grabby-voice.hf.space'; | |
| function basicHeader(username, password) { | |
| return 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'); | |
| } | |
| function parseBasicAuth(header) { | |
| if (!header || !header.startsWith('Basic ')) return null; | |
| try { | |
| const decoded = Buffer.from(header.slice('Basic '.length), 'base64').toString('utf8'); | |
| const splitAt = decoded.indexOf(':'); | |
| if (splitAt < 0) return null; | |
| return { username: decoded.slice(0, splitAt), password: decoded.slice(splitAt + 1) }; | |
| } catch { | |
| return null; | |
| } | |
| } | |
| function unauthorized(res) { | |
| res.writeHead(401, { | |
| 'WWW-Authenticate': 'Basic realm="Grabby Voice Admin", charset="UTF-8"', | |
| 'Content-Type': 'text/plain; charset=utf-8', | |
| }); | |
| res.end('Authentication required'); | |
| } | |
| function isAuthorized(req) { | |
| const expectedUsername = process.env.ADMIN_USERNAME; | |
| const expectedPassword = process.env.ADMIN_PASSWORD; | |
| if (!expectedUsername || !expectedPassword) return false; | |
| const credentials = parseBasicAuth(req.headers.authorization); | |
| return credentials?.username === expectedUsername && credentials?.password === expectedPassword; | |
| } | |
| function copyRequestHeaders(req) { | |
| const headers = new Headers(); | |
| for (const [key, value] of Object.entries(req.headers)) { | |
| if (!value) continue; | |
| const lower = key.toLowerCase(); | |
| if (['host', 'connection', 'content-length', 'authorization'].includes(lower)) continue; | |
| headers.set(key, Array.isArray(value) ? value.join(', ') : value); | |
| } | |
| const upstreamUser = process.env.UPSTREAM_ADMIN_USERNAME || process.env.ADMIN_USERNAME; | |
| const upstreamPass = process.env.UPSTREAM_ADMIN_PASSWORD || process.env.ADMIN_PASSWORD; | |
| if (upstreamUser && upstreamPass) headers.set('authorization', basicHeader(upstreamUser, upstreamPass)); | |
| return headers; | |
| } | |
| function copyResponseHeaders(upstream) { | |
| const headers = {}; | |
| upstream.headers.forEach((value, key) => { | |
| const lower = key.toLowerCase(); | |
| if (['connection', 'content-encoding', 'content-length', 'transfer-encoding'].includes(lower)) return; | |
| headers[key] = value; | |
| }); | |
| return headers; | |
| } | |
| const server = http.createServer(async (req, res) => { | |
| if (!isAuthorized(req)) { | |
| unauthorized(res); | |
| return; | |
| } | |
| if (req.url === '/' || req.url === '') { | |
| res.writeHead(302, { Location: '/admin/reviews' }); | |
| res.end(); | |
| return; | |
| } | |
| if (!['GET', 'HEAD'].includes(req.method || 'GET')) { | |
| res.writeHead(405, { 'Content-Type': 'text/plain; charset=utf-8' }); | |
| res.end('Method not allowed'); | |
| return; | |
| } | |
| try { | |
| const target = new URL(req.url || '/', upstreamBase); | |
| const upstream = await fetch(target, { | |
| method: req.method, | |
| headers: copyRequestHeaders(req), | |
| redirect: 'follow', | |
| }); | |
| res.writeHead(upstream.status, copyResponseHeaders(upstream)); | |
| if (req.method === 'HEAD' || !upstream.body) { | |
| res.end(); | |
| return; | |
| } | |
| const reader = upstream.body.getReader(); | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| res.write(Buffer.from(value)); | |
| } | |
| res.end(); | |
| } catch (error) { | |
| console.error(error); | |
| res.writeHead(502, { 'Content-Type': 'text/plain; charset=utf-8' }); | |
| res.end('Admin upstream is unavailable'); | |
| } | |
| }); | |
| server.listen(port, '0.0.0.0', () => { | |
| console.log(`Grabby admin proxy listening on ${port}`); | |
| }); | |