mirror of
https://git.waldn.net/git/knotteye/satyr.git
synced 2025-09-16 06:34:57 +00:00
I don't even know.
Filled out API for user management Proper escaping of user input for SQL queries (stream keys aren't user input) Filled out frontend with profile management, vods, etc. I don't remember there's probably more, fuck.
This commit is contained in:
55
src/api.ts
55
src/api.ts
@ -5,35 +5,44 @@ function init(conf: object){
|
||||
config = conf;
|
||||
}
|
||||
|
||||
async function register(name: string, password: string, streamer: boolean) {
|
||||
if(!config.registration){
|
||||
return {"error":"registration disabled"};
|
||||
}
|
||||
else {
|
||||
if(name.includes(';') || name.includes(' ')) return {"error":"illegal characters"};
|
||||
let s: boolean;
|
||||
if(streamer && config.streamKeys) s = true;
|
||||
else s = false;
|
||||
let r: boolean = await db.addUser(name, password, s, false);
|
||||
if(r) return {"success":""};
|
||||
else return {"error":""};
|
||||
async function register(name: string, password: string, confirm: string) {
|
||||
if(!config.registration) return {"error":"registration disabled"};
|
||||
if(name.includes(';') || name.includes(' ') || name.includes('\'')) return {"error":"illegal characters"};
|
||||
if(password !== confirm) return {"error":"mismatched passwords"};
|
||||
for(let i=0;i<config.restrictedNames.length;i++){
|
||||
if (name === config.restrictedNames[i]) return {"error":"restricted name"};
|
||||
}
|
||||
let r: boolean = await db.addUser(name, password);
|
||||
if(r) return {"success":""};
|
||||
return {"error":""};
|
||||
}
|
||||
|
||||
async function login(name: string, pass: string) {
|
||||
return await db.validatePassword(name, pass);
|
||||
async function update(name: string, password: string, title: string, bio: string, record: boolean){
|
||||
if(!name || !password) return {"error":"Insufficient parameters"};
|
||||
let auth: boolean = await db.validatePassword(name, password);
|
||||
if(!auth) return {"error":"Username or Password Incorrect"};
|
||||
await db.query('UPDATE user_meta set title='+db.raw.escape(title)+', about='+db.raw.escape(bio)+' where username='+db.raw.escape(name));
|
||||
if(!record) await db.query('UPDATE users set record_flag=false where username='+db.raw.escape(name));
|
||||
else await db.query('UPDATE users set record_flag=true where username='+db.raw.escape(name));
|
||||
return {"success":""};
|
||||
}
|
||||
|
||||
async function users(num: number) {
|
||||
return await db.query('select username from users limit '+num);
|
||||
async function changepwd(name: string, password: string, newpwd: string){
|
||||
if(!name || !password) return {"error":"Insufficient parameters"};
|
||||
let auth: boolean = await db.validatePassword(name, password);
|
||||
if(!auth) return {"error":"Username or Password Incorrect"};
|
||||
let newhash: string = await db.hash(newpwd);
|
||||
await db.query('UPDATE users set password_hash='+db.raw.escape(newhash)+'where username='+db.raw.escape(name)+' limit 1');
|
||||
return {"success":""};
|
||||
}
|
||||
|
||||
async function user(name: string) {
|
||||
|
||||
async function changesk(name: string, password: string){
|
||||
if(!name || !password) return {"error":"Insufficient parameters"};
|
||||
let auth: boolean = await db.validatePassword(name, password);
|
||||
if(!auth) return {"error":"Username or Password Incorrect"};
|
||||
let key: string = await db.genKey();
|
||||
await db.query('UPDATE users set stream_key='+db.raw.escape(key)+'where username='+db.raw.escape(name)+' limit 1');
|
||||
return {"success":key};
|
||||
}
|
||||
|
||||
async function instance() {
|
||||
|
||||
}
|
||||
|
||||
export { init, register };
|
||||
export { init, register, update, changepwd, changesk };
|
20
src/cli.ts
20
src/cli.ts
@ -6,8 +6,6 @@ db.init(config.database, config.bcrypt);
|
||||
|
||||
flags.defineString('add', '', 'User to add');
|
||||
flags.defineString('remove', '', 'User to remove');
|
||||
flags.defineString('mkstreamer', '', 'Give a stream key to a user');
|
||||
flags.defineString('rmstreamer', '', 'Remove a stream key from a user');
|
||||
flags.defineString('password', '', 'password to hash');
|
||||
flags.defineBoolean('admin');
|
||||
flags.defineBoolean('streamer');
|
||||
@ -15,7 +13,7 @@ flags.defineBoolean('streamer');
|
||||
flags.parse();
|
||||
|
||||
if(flags.get('add') !== ''){
|
||||
db.addUser(flags.get('add'), flags.get('password'), flags.get('streamer'), flags.get('admin')).then((result) => {
|
||||
db.addUser(flags.get('add'), flags.get('password')).then((result) => {
|
||||
if(result) console.log("User added successfully.");
|
||||
else console.log("Could not add user. Is the password field empty?");
|
||||
process.exit();
|
||||
@ -28,20 +26,4 @@ if(flags.get('remove') !== ''){
|
||||
else console.log("Could not remove user.");
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
|
||||
if(flags.get('mkstreamer') !== ''){
|
||||
db.addStreamKey(flags.get('mkstreamer')).then((result) => {
|
||||
if(result) console.log("Key added successfully.");
|
||||
else console.log("Could not add key.");
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
|
||||
if(flags.get('rmstreamer') !== ''){
|
||||
db.rmStreamKey(flags.get('rmstreamer')).then((result) => {
|
||||
if(result) console.log("Key removed successfully.");
|
||||
else console.log("Could not remove key.");
|
||||
process.exit();
|
||||
});
|
||||
}
|
@ -11,13 +11,14 @@ function run(): void{
|
||||
const satyr: object = {
|
||||
privateEndpoint: config.media.privateEndpoint,
|
||||
record: config.media.record,
|
||||
streamKeys: config.media.streamKeys,
|
||||
registration: config.satyr.registration,
|
||||
webFormat: config.satyr.webFormat,
|
||||
restrictedNames: config.satyr.restrictedNames,
|
||||
name: config.satyr.name,
|
||||
domain: config.satyr.domain,
|
||||
email: config.satyr.email
|
||||
email: config.satyr.email,
|
||||
rootredirect: config.satyr.rootredirect,
|
||||
version: process.env.npm_package_version
|
||||
};
|
||||
const nms: object = {
|
||||
logType: config.server.logs,
|
||||
|
@ -10,24 +10,23 @@ function init (db: object, bcrypt: object){
|
||||
cryptoconfig = bcrypt;
|
||||
}
|
||||
|
||||
async function addUser(name: string, password: string, streamer: boolean, admin: boolean){
|
||||
async function addUser(name: string, password: string){
|
||||
//does not respect registration setting in config
|
||||
//nor stream keys
|
||||
if(password === '') return false;
|
||||
let key: string = ' ';
|
||||
if (streamer) key = await genKey();
|
||||
let key: string = await genKey();
|
||||
let hash: string = await bcrypt.hash(password, cryptoconfig.saltRounds);
|
||||
let dupe = await query('select * from users where username=\''+name+'\'');
|
||||
let dupe = await query('select * from users where username='+raw.escape(name));
|
||||
if(dupe[0]) return false;
|
||||
let q: string = 'INSERT INTO users (username, password_hash, stream_key, record_flag, is_mod) VALUES (\''+name+'\', \''+hash+'\', \''+key+'\', 0, '+admin+')';
|
||||
await query(q);
|
||||
await query('INSERT INTO users (username, password_hash, stream_key, record_flag) VALUES ('+raw.escape(name)+', '+raw.escape(hash)+', '+raw.escape(key)+', 0');
|
||||
await query('INSERT INTO user_meta (username, title, about, live) VALUES ('+raw.escape(name)+',\'\',\'\',false)');
|
||||
return true;
|
||||
}
|
||||
|
||||
async function rmUser(name: string){
|
||||
let exist = await query('select * from users where username=\''+name+'\'');
|
||||
let exist = await query('select * from users where username='+raw.escape(name));
|
||||
if(!exist[0]) return false;
|
||||
await query('delete from users where username=\''+name+'\' limit 1');
|
||||
await query('delete from users where username='+raw.escape(name)+' limit 1');
|
||||
await query('delete from user_meta where username='+raw.escape(name)+' limit 1');
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -38,21 +37,6 @@ async function genKey(){
|
||||
else return key;
|
||||
}
|
||||
|
||||
async function addStreamKey(name: string){
|
||||
let exist = await query('select * from users where username=\''+name+'\'');
|
||||
if(!exist[0]) return false;
|
||||
let key = await genKey();
|
||||
await query('update users set stream_key=\''+key+'\' where username=\''+name+'\' limit 1');
|
||||
return true;
|
||||
}
|
||||
|
||||
async function rmStreamKey(name: string){
|
||||
let exist = await query('select * from users where username=\''+name+'\'');
|
||||
if(!exist[0]) return false;
|
||||
await query('update users set stream_key=\'\' where username=\''+name+'\' limit 1');
|
||||
return true;
|
||||
}
|
||||
|
||||
async function query(query: string){
|
||||
return new Promise(resolve => raw.query(query, (error, results, fields) => {
|
||||
if(error) throw error;
|
||||
@ -61,8 +45,12 @@ async function query(query: string){
|
||||
}
|
||||
|
||||
async function validatePassword(username: string, password: string){
|
||||
let pass: any= await query('select password from users where username=\''+username+'\' limit 1');
|
||||
return await bcrypt.compare(password, pass[0].password_hash);
|
||||
let pass: any = await query('select password_hash from users where username='+raw.escape(username)+' limit 1');
|
||||
return await bcrypt.compare(password, pass[0].password_hash.toString());
|
||||
}
|
||||
|
||||
export { query, raw, init, addUser, rmUser, addStreamKey, rmStreamKey, validatePassword };
|
||||
async function hash(pwd){
|
||||
return await bcrypt.hash(pwd, cryptoconfig.saltRounds);
|
||||
}
|
||||
|
||||
export { query, raw, init, addUser, rmUser, validatePassword, hash, genKey };
|
92
src/http.ts
92
src/http.ts
@ -1,6 +1,7 @@
|
||||
import * as express from "express";
|
||||
import * as njk from "nunjucks";
|
||||
import * as bodyparser from "body-parser";
|
||||
import * as fs from "fs";
|
||||
import * as api from "./api";
|
||||
import * as db from "./database";
|
||||
|
||||
@ -18,30 +19,95 @@ function init(satyr: any){
|
||||
sitename: satyr.name,
|
||||
domain: satyr.domain,
|
||||
email: satyr.email,
|
||||
user: '',
|
||||
streamtitle: '',
|
||||
rootredirect: satyr.rootredirect,
|
||||
version: satyr.version
|
||||
};
|
||||
app.use(bodyparser.json());
|
||||
app.use(bodyparser.urlencoded({ extended: true }));
|
||||
//site handlers
|
||||
app.get('/', (req, res) => {
|
||||
res.render('index.njk', njkconf);
|
||||
res.redirect(njkconf.rootredirect);
|
||||
});
|
||||
app.get('/about', (req, res) => {
|
||||
res.render('about.njk', njkconf);
|
||||
});
|
||||
app.get('/users/*', (req, res) => {
|
||||
njkconf.user = req.url.split('/')[2].toLowerCase();
|
||||
res.render('user.njk', njkconf);
|
||||
});
|
||||
app.get('/registration', (req, res) => {
|
||||
res.render('registration.njk', njkconf);
|
||||
});
|
||||
app.post('/api/register', (req, res) => {
|
||||
api.register(req.body.username, req.body.password, req.body.streamer).then( (result) => {
|
||||
res.send({"error":""});
|
||||
app.get('/users', (req, res) => {
|
||||
db.query('select username from users').then((result) => {
|
||||
njkconf.list = result;
|
||||
res.render('list.njk', njkconf);
|
||||
njkconf.list = '';
|
||||
});
|
||||
});
|
||||
app.get('/users/live', (req, res) => {
|
||||
db.query('select username,title from user_meta where live=1;').then((result) => {
|
||||
njkconf.list = result;
|
||||
res.render('live.njk', njkconf);
|
||||
njkconf.list = '';
|
||||
});
|
||||
});
|
||||
app.get('/users/*', (req, res) => {
|
||||
njkconf.user = req.url.split('/')[2].toLowerCase();
|
||||
db.query('select title,about from user_meta where username='+db.raw.escape(njkconf.user)).then((result) => {
|
||||
if(result[0]){
|
||||
njkconf.streamtitle = result[0].title;
|
||||
njkconf.about = result[0].about;
|
||||
res.render('user.njk', njkconf);
|
||||
}
|
||||
else res.render('404.njk', njkconf);
|
||||
});
|
||||
});
|
||||
app.get('/vods/*', (req, res) => {
|
||||
njkconf.user = req.url.split('/')[2].toLowerCase();
|
||||
db.query('select username from user_meta where username='+db.raw.escape(njkconf.user)).then((result) => {
|
||||
if(result[0]){
|
||||
fs.readdir('./site/live/'+njkconf.user, {withFileTypes: true} , (err, files) => {
|
||||
if(files) njkconf.list = files.filter(fn => fn.name.endsWith('.mp4'));
|
||||
else njkconf.list = [];
|
||||
res.render('vods.njk', njkconf);
|
||||
});
|
||||
}
|
||||
else res.render('404.njk', njkconf);
|
||||
});
|
||||
});
|
||||
app.get('/register', (req, res) => {
|
||||
res.render('registration.njk', njkconf);
|
||||
});
|
||||
app.get('/profile', (req, res) => {
|
||||
res.render('profile.njk', njkconf);
|
||||
});
|
||||
app.get('/changepwd', (req, res) => {
|
||||
res.render('changepwd.njk', njkconf);
|
||||
});
|
||||
app.get('/changesk', (req, res) => {
|
||||
res.render('changesk.njk', njkconf);
|
||||
});
|
||||
//api handlers
|
||||
app.post('/api/register', (req, res) => {
|
||||
api.register(req.body.username, req.body.password, req.body.confirm).then( (result) => {
|
||||
res.send(result);
|
||||
});
|
||||
});
|
||||
app.post('/api/user', (req, res) => {
|
||||
api.update(req.body.username, req.body.password, req.body.title, req.body.bio, req.body.record).then((result) => {
|
||||
res.send(result);
|
||||
});
|
||||
});
|
||||
app.post('/api/user/password', (req, res) => {
|
||||
api.changepwd(req.body.username, req.body.password, req.body.newpassword).then((result) => {
|
||||
res.send(result);
|
||||
});
|
||||
});
|
||||
app.post('/api/user/streamkey', (req, res) => {
|
||||
api.changesk(req.body.username, req.body.password).then((result) => {
|
||||
res.send(result);
|
||||
})
|
||||
});
|
||||
//static files if nothing else matches first
|
||||
app.use(express.static('site'));
|
||||
//404 Handler
|
||||
app.use(function (req, res, next) {
|
||||
res.status(404).render('404.njk', njkconf);
|
||||
});
|
||||
}
|
||||
|
||||
export { init };
|
@ -8,7 +8,7 @@ function init (mediaconfig: any, satyrconfig: any) {
|
||||
nms.run();
|
||||
|
||||
nms.on('postPublish', (id, StreamPath, args) => {
|
||||
console.log("[NodeMediaServer] Prepublish Hook for stream:",id);
|
||||
console.log("[NodeMediaServer] Publish Hook for stream:",id);
|
||||
let session = nms.getSession(id);
|
||||
let app: string = StreamPath.split("/")[1];
|
||||
let key: string = StreamPath.split("/")[2];
|
||||
@ -31,6 +31,8 @@ function init (mediaconfig: any, satyrconfig: any) {
|
||||
return false;
|
||||
}
|
||||
console.log("[NodeMediaServer] Public endpoint, checking record flag.");
|
||||
//set live flag
|
||||
db.query('update user_meta set live=true where username=\''+key+'\' limit 1');
|
||||
//if this stream is from the public endpoint, check if we should be recording
|
||||
return db.query('select username,record_flag from users where username=\''+key+'\' limit 1').then((results) => {
|
||||
if(results[0].record_flag && satyrconfig.record){
|
||||
@ -61,7 +63,11 @@ function init (mediaconfig: any, satyrconfig: any) {
|
||||
//if the url is formatted correctly and the user is streaming to the correct private endpoint
|
||||
//grab the username from the database and redirect the stream there if the key is valid
|
||||
//otherwise kill the session
|
||||
db.query('select username from users where stream_key=\''+key+'\' limit 1').then((results) => {
|
||||
if(key.includes(' ')) {
|
||||
session.reject();
|
||||
return false;
|
||||
}
|
||||
db.query('select username from users where stream_key='+db.raw.escape(key)+' limit 1').then((results) => {
|
||||
if(results[0]){
|
||||
exec('ffmpeg -analyzeduration 0 -i rtmp://127.0.0.1:'+mediaconfig.rtmp.port+'/'+satyrconfig.privateEndpoint+'/'+key+' -vcodec copy -acodec copy -crf 18 -f flv rtmp://127.0.0.1:'+mediaconfig.rtmp.port+'/'+mediaconfig.trans.tasks[0].app+'/'+results[0].username);
|
||||
console.log('[NodeMediaServer] Stream key okay for stream:',id);
|
||||
@ -72,6 +78,13 @@ function init (mediaconfig: any, satyrconfig: any) {
|
||||
}
|
||||
});
|
||||
});
|
||||
nms.on('donePublish', (id, StreamPath, args) => {
|
||||
let app: string = StreamPath.split("/")[1];
|
||||
let key: string = StreamPath.split("/")[2];
|
||||
if(app === mediaconfig.trans.tasks[0].app) {
|
||||
db.query('update user_meta set live=false where username=\''+key+'\' limit 1');
|
||||
}
|
||||
});
|
||||
nms.on('prePlay', (id, StreamPath, args) => {
|
||||
let session = nms.getSession(id);
|
||||
let app: string = StreamPath.split("/")[1];
|
||||
|
Reference in New Issue
Block a user