remoteview
Version:
Remote isolated browser platform by Dosyago
239 lines (217 loc) • 5.7 kB
JavaScript
;
import fs from 'fs';
import path from 'path';
import childProcess from 'child_process';
import http from 'http';
import https from 'https';
import helmet from 'helmet';
import compression from 'compression';
import cookieParser from 'cookie-parser';
import rateLimit from 'express-rate-limit';
import csrf from 'csurf';
import express from 'express';
import {APP_ROOT, GO_SECURE, COOKIENAME} from '../common.js';
const DEBUG = {
val: 1,
mode: 'dev',
goSecure: true
}
const argv = process.argv;
const device = argv[3] || 'rtp.monitor' || 'auto_null.monitor';
const encoders = {
mp3: {
contentType: 'audio/mpeg',
command: 'lame',
args: ['-f', '-r', '-B', '20', '-', '-']
},
flac: {
contentType: 'audio/flac',
command: 'flac',
args: [
'--endian=little',
'--sign=signed',
'--sample-rate=44100',
'--bps=16',
'--channels=2',
'--silent',
'-'
]
}
};
const encoderType = 'mp3';
//var hooks = 0;
let parec = undefined;
let encoder = undefined;
const sslBranch = 'master'
const SSL_OPTS = {};
const COOKIE_OPTS = {
secure: true,
httpOnly: true,
maxAge: 345600000,
sameSite: 'None'
};
const RateLimiter = rateLimit({
windowMs: 1000 * 60 * 3,
max: DEBUG.mode == 'dev' ? 1000 : 300
});
let certsFound = false;
if ( DEBUG.goSecure ) {
try {
Object.assign(SSL_OPTS, {
cert: fs.readFileSync(`../sslcert/${sslBranch}/fullchain.pem`),
key: fs.readFileSync(`../sslcert/${sslBranch}/privkey.pem`),
ca: fs.readFileSync(`../sslcert/${sslBranch}/chain.pem`),
});
certsFound = true;
} catch(e) {
DEBUG.val && console.warn(e);
}
DEBUG.val && console.log(SSL_OPTS, {GO_SECURE});
}
function getEncoder() {
if (encoder) return encoder;
DEBUG.val && console.log('starting encoder');
parec = childProcess.spawn('parec', ['--process-time-msec=100', '--latency-msec=100', '-d', device]);
parec.stderr.pipe(process.stdout);
parec.on('error', e => {
console.log('parec error', e);
encoder = null;
});
parec.on('close', e => {
console.log('parec close', e);
encoder = null;
});
parec.on('exit', e => {
console.log('parec exit', e);
encoder = null;
});
const encoderCommand = encoders[encoderType];
encoder = childProcess.spawn(encoderCommand.command, encoderCommand.args);
encoder.stderr.pipe(process.stdout);
encoder.on('error', e => {
console.log('encoder error', e);
encoder = null;
});
encoder.on('close', e => {
console.log('encoder close', e);
encoder = null;
});
encoder.on('exit', e => {
console.log('encoder exit', e);
encoder = null;
});
parec.stdout.pipe(encoder.stdin);
return encoder;
}
/*function releaseEncoder() {
hooks--;
if (hooks > 0) return;
DEBUG.val && console.log('killing encoder because nobody is listening');
encoder.kill('SIGINT');
parec.kill('SIGINT');
encoder = undefined;
parec = undefined;
}*/
const port = parseInt(argv[2]);
const PORT = port;
const COOKIE = process.argv[4];
const TOKEN = process.argv[5];
DEBUG.val && console.log('starting http server on port', port);
const MODE = certsFound ? https: http;
const app = express();
app.use(helmet({
frameguard: false,
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
imgSrc: [
"'self'",
"data:",
"blob:"
],
mediaSrc: [
"'self'",
"https://*.dosyago.com:*"
],
frameSrc: [
"'self'",
`https://*.dosyago.com:${PORT+1}`,
`https://*.dosyago.com:${PORT+2}`,
`https://*.dosyago.com:${PORT+3}`,
],
connectSrc: [
"'self'",
"wss://*.dosyago.com:*",
`https://*.dosyago.com:${PORT+1}`
],
styleSrc: [
"'self'",
"'unsafe-inline'"
],
scriptSrc: [
"'self'",
"'unsafe-eval'",
"'sha256-ktnwD9kIpbxpOmbTg7NUsKRlpicCv8bryYhIbiRDFaQ='",
],
objectSrc: ["'none'"],
},
reportOnly: false,
}
}));
app.use(compression());
app.use(RateLimiter);
app.use(express.urlencoded({extended:true}));
app.use(cookieParser());
app.use(csrf({cookie:{sameSite:'None', secure:true}}));
app.use(express.static(path.resolve(APP_ROOT, 'parec-server', 'public')));
app.get('/login', (req, res) => {
res.type('html');
const {token} = req.query;
const cookie = req.cookies[COOKIENAME+PORT];
let loggedIn = false;
if ( token == TOKEN ) {
res.cookie(COOKIENAME+PORT, COOKIE, COOKIE_OPTS);
loggedIn = true;
} else if ( cookie == COOKIE ) {
loggedIn = true;
}
if ( loggedIn ) {
res.end(`
<!DOCTYPE html>
<script src=request_audio.js></script>
`);
} else {
res.end(`
<!DOCTYPE html>
<script src=request_login.js></script>
`);
}
});
app.get('/', (request, response) => {
const cookie = request.cookies[COOKIENAME+PORT];
if ( cookie == COOKIE ) {
var contentType = encoders[encoderType].contentType;
DEBUG.val && console.log(' setting Content-Type to', contentType);
response.writeHead(200, {
'Connection': 'keep-alive',
'Content-Type': contentType
});
var encoder = getEncoder();
//encoder.stdout.pipe(response);
encoder.stdout.on('data', function(buffer) {
response.write(buffer);
});
request.on('close', function() {
//response.end();
//releaseEncoder();
});
} else {
response.sendStatus(401);
}
});
const server = MODE.createServer(SSL_OPTS, app).listen(port);
server.on('connection', function(socket) {
DEBUG.val && console.log('New connection: setting no delay true');
socket.setNoDelay(true);
});