Big Commit!
Seriously, this one is pretty massive. Satyr now has proper sessions in the browser (like a real website), and a lot of changes were made. API Endpoints were changed from requiring a username and password to requiring a valid JsonWebToken, obtained from /api/login Satyr will generate a PEM format key for JWT signing and verification on startup if it can't find one at config/jwt.pem This file was added to .gitignore Two new depencies: cookie-parser and jose, for reading and signing JWTs. Refactored http.ts into mutiple functions, with a couple helper functions related to cookies and JWT decoding and verification. Socket.IO chat will also automatically log in users with a valid JWT. Refactor api.ts to reflect new requirements from endpoints. Minor bugfix in server.ts so we don't throw an uncaught exception when rejecting a stream with an invalid key. Transcode options readded to default.toml. They do nothing and they are not sane defaults. Both of those things are in the todo list.merge-requests/6/head
parent
31426a0c41
commit
25cf8a37a2
|
@ -1,6 +1,7 @@
|
||||||
node_modules
|
node_modules
|
||||||
site/live
|
site/live
|
||||||
config/local.toml
|
config/local.toml
|
||||||
|
config/jwt.pem
|
||||||
config/generated.toml
|
config/generated.toml
|
||||||
install/db_setup.sql
|
install/db_setup.sql
|
||||||
build/**
|
build/**
|
||||||
|
|
|
@ -51,3 +51,8 @@ record = false
|
||||||
publicEndpoint = 'live'
|
publicEndpoint = 'live'
|
||||||
privateEndpoint = 'stream'
|
privateEndpoint = 'stream'
|
||||||
ffmpeg = ''
|
ffmpeg = ''
|
||||||
|
|
||||||
|
[transcode]
|
||||||
|
adapative = false
|
||||||
|
variants = 3
|
||||||
|
format = 'dash'
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "satyr",
|
"name": "satyr",
|
||||||
"version": "0.4.3",
|
"version": "0.4.4",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -121,6 +121,16 @@
|
||||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
||||||
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
|
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
|
||||||
},
|
},
|
||||||
|
"asn1.js": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-Q7hnYGGNYbcmGrCPulXfkEw7oW7qjWeM4ZTALmgpuIcZLxyqqKYWxCZg2UBm8bklrnB4m2mGyJPWfoktdORD8A==",
|
||||||
|
"requires": {
|
||||||
|
"bn.js": "^4.0.0",
|
||||||
|
"inherits": "^2.0.1",
|
||||||
|
"minimalistic-assert": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"assign-symbols": {
|
"assign-symbols": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
|
||||||
|
@ -257,6 +267,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
||||||
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
|
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
|
||||||
},
|
},
|
||||||
|
"bn.js": {
|
||||||
|
"version": "4.11.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
||||||
|
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
|
||||||
|
},
|
||||||
"body-parser": {
|
"body-parser": {
|
||||||
"version": "1.19.0",
|
"version": "1.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||||
|
@ -507,6 +522,22 @@
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
||||||
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
||||||
},
|
},
|
||||||
|
"cookie-parser": {
|
||||||
|
"version": "1.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz",
|
||||||
|
"integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==",
|
||||||
|
"requires": {
|
||||||
|
"cookie": "0.3.1",
|
||||||
|
"cookie-signature": "1.0.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
||||||
|
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"cookie-signature": {
|
"cookie-signature": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
@ -1837,6 +1868,14 @@
|
||||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"jose": {
|
||||||
|
"version": "1.15.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jose/-/jose-1.15.1.tgz",
|
||||||
|
"integrity": "sha512-Gp+53zIEb68qTuyagcalDMVn1s0WrxiGBQJbEjShOdv3CYmbPIJEAN0Qtn4rCa7XgODoEa7HHuz8GoYgIpIzog==",
|
||||||
|
"requires": {
|
||||||
|
"asn1.js": "^5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"json5": {
|
"json5": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||||
|
@ -1940,6 +1979,11 @@
|
||||||
"mime-db": "1.40.0"
|
"mime-db": "1.40.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"minimalistic-assert": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
||||||
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
|
@ -2984,8 +3028,7 @@
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "3.6.3",
|
"version": "3.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
|
||||||
"integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
|
"integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"union-value": {
|
"union-value": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
|
|
@ -17,9 +17,11 @@
|
||||||
"bcrypt": "^3.0.6",
|
"bcrypt": "^3.0.6",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"config": "^3.2.2",
|
"config": "^3.2.2",
|
||||||
|
"cookie-parser": "^1.4.4",
|
||||||
"dirty": "^1.1.0",
|
"dirty": "^1.1.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"flags": "^0.1.3",
|
"flags": "^0.1.3",
|
||||||
|
"jose": "^1.15.1",
|
||||||
"mysql": "^2.17.1",
|
"mysql": "^2.17.1",
|
||||||
"node-media-server": ">=2.1.3 <3.0.0",
|
"node-media-server": ">=2.1.3 <3.0.0",
|
||||||
"nunjucks": "^3.2.0",
|
"nunjucks": "^3.2.0",
|
||||||
|
|
39
src/api.ts
39
src/api.ts
|
@ -1,4 +1,5 @@
|
||||||
import * as db from "./database"
|
import * as db from "./database"
|
||||||
|
import { unregisterUser } from "./irc";
|
||||||
|
|
||||||
var config: any;
|
var config: any;
|
||||||
function init(conf: object){
|
function init(conf: object){
|
||||||
|
@ -20,18 +21,26 @@ async function register(name: string, password: string, confirm: string) {
|
||||||
return {"error":""};
|
return {"error":""};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update(name: string, password: string, title: string, bio: string, record: boolean){
|
async function update(fields: object){
|
||||||
if(!name || !password) return {"error":"Insufficient parameters"};
|
if(!fields['title'] && !fields['bio'] && (fields['rec'] !== 'true' && fields['rec'] !== 'false')) return {"error":"no valid fields specified"};
|
||||||
let auth: boolean = await db.validatePassword(name, password);
|
let qs: string = "";
|
||||||
if(!auth) return {"error":"Username or Password Incorrect"};
|
let f: boolean = false;
|
||||||
await db.query('UPDATE user_meta set title='+db.raw.escape(title)+', about='+db.raw.escape(bio)+' where username='+db.raw.escape(name));
|
if(fields['title']) {qs += ' user_meta.title='+db.raw.escape(fields['title']);f = true;}
|
||||||
if(!record) await db.query('UPDATE users set record_flag=false where username='+db.raw.escape(name));
|
if(fields['bio']) {
|
||||||
else await db.query('UPDATE users set record_flag=true where username='+db.raw.escape(name));
|
if(f) qs+=',';
|
||||||
|
qs += ' user_meta.about='+db.raw.escape(fields['bio']);
|
||||||
|
f=true;
|
||||||
|
}
|
||||||
|
if(typeof(fields['rec']) === 'boolean' || typeof(fields['rec']) === 'number') {
|
||||||
|
if(f) qs+=',';
|
||||||
|
qs += ' users.record_flag='+db.raw.escape(fields['rec']);
|
||||||
|
}
|
||||||
|
await db.query('UPDATE users,user_meta SET'+qs+' WHERE users.username='+db.raw.escape(fields['name'])+' AND user_meta.username='+db.raw.escape(fields['name']));
|
||||||
return {"success":""};
|
return {"success":""};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changepwd(name: string, password: string, newpwd: string){
|
async function changepwd(name: string, password: string, newpwd: string){
|
||||||
if(!name || !password) return {"error":"Insufficient parameters"};
|
if(!name || !password || !newpwd) return {"error":"Insufficient parameters"};
|
||||||
let auth: boolean = await db.validatePassword(name, password);
|
let auth: boolean = await db.validatePassword(name, password);
|
||||||
if(!auth) return {"error":"Username or Password Incorrect"};
|
if(!auth) return {"error":"Username or Password Incorrect"};
|
||||||
let newhash: string = await db.hash(newpwd);
|
let newhash: string = await db.hash(newpwd);
|
||||||
|
@ -39,13 +48,17 @@ async function changepwd(name: string, password: string, newpwd: string){
|
||||||
return {"success":""};
|
return {"success":""};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changesk(name: string, password: string){
|
async function changesk(name: 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();
|
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');
|
await db.query('UPDATE users set stream_key='+db.raw.escape(key)+'where username='+db.raw.escape(name)+' limit 1');
|
||||||
return {"success":key};
|
return {"success":key};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { init, register, update, changepwd, changesk };
|
async function login(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"};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { init, register, update, changepwd, changesk, login };
|
332
src/http.ts
332
src/http.ts
|
@ -4,22 +4,35 @@ import * as bodyparser from "body-parser";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import * as socketio from "socket.io";
|
import * as socketio from "socket.io";
|
||||||
import * as http from "http";
|
import * as http from "http";
|
||||||
|
import * as cookies from "cookie-parser";
|
||||||
import * as dirty from "dirty";
|
import * as dirty from "dirty";
|
||||||
import * as api from "./api";
|
import * as api from "./api";
|
||||||
import * as db from "./database";
|
import * as db from "./database";
|
||||||
import * as irc from "./irc";
|
import * as irc from "./irc";
|
||||||
|
import { readFileSync, writeFileSync } from "fs";
|
||||||
|
import { JWT, JWK } from "jose";
|
||||||
|
import { strict } from "assert";
|
||||||
|
import { parse } from "path";
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
const io = socketio(server);
|
const io = socketio(server);
|
||||||
const store = dirty();
|
const store = dirty();
|
||||||
|
var jwkey;
|
||||||
|
try{
|
||||||
|
jwkey = JWK.asKey(readFileSync('./config/jwt.pem'));
|
||||||
|
} catch (e) {
|
||||||
|
console.log("No key found for JWT signing, generating one now.");
|
||||||
|
jwkey = JWK.generateSync('RSA', 2048, { use: 'sig' });
|
||||||
|
writeFileSync('./config/jwt.pem', jwkey.toPEM(true));
|
||||||
|
}
|
||||||
var njkconf;
|
var njkconf;
|
||||||
|
|
||||||
async function init(satyr: any, port: number, ircconf: any){
|
async function init(satyr: any, port: number, ircconf: any){
|
||||||
njk.configure('templates', {
|
njk.configure('templates', {
|
||||||
autoescape : true,
|
autoescape : true,
|
||||||
express : app,
|
express : app,
|
||||||
watch: false
|
watch : true
|
||||||
});
|
});
|
||||||
njkconf ={
|
njkconf ={
|
||||||
sitename: satyr.name,
|
sitename: satyr.name,
|
||||||
|
@ -28,88 +41,287 @@ async function init(satyr: any, port: number, ircconf: any){
|
||||||
rootredirect: satyr.rootredirect,
|
rootredirect: satyr.rootredirect,
|
||||||
version: satyr.version
|
version: satyr.version
|
||||||
};
|
};
|
||||||
|
app.use(cookies());
|
||||||
app.use(bodyparser.json());
|
app.use(bodyparser.json());
|
||||||
app.use(bodyparser.urlencoded({ extended: true }));
|
app.use(bodyparser.urlencoded({ extended: true }));
|
||||||
//site handlers
|
//site handlers
|
||||||
|
await initSite(satyr.registration);
|
||||||
|
//api handlers
|
||||||
|
await initAPI();
|
||||||
|
//static files if nothing else matches first
|
||||||
|
app.use(express.static(satyr.directory));
|
||||||
|
//404 Handler
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
|
res.status(404).render('404.njk', Object.assign({auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
|
}
|
||||||
|
else res.status(404).render('404.njk', njkconf);
|
||||||
|
//res.status(404).render('404.njk', njkconf);
|
||||||
|
});
|
||||||
|
await initChat(ircconf);
|
||||||
|
server.listen(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function newNick(socket, skip?: boolean) {
|
||||||
|
if(socket.handshake.headers['cookie'] && !skip){
|
||||||
|
let c = await parseCookie(socket.handshake.headers['cookie']);
|
||||||
|
let t = await validToken(c['Authorization']);
|
||||||
|
if(t) return t['username'];
|
||||||
|
}
|
||||||
|
//i just realized how shitty of an idea this is
|
||||||
|
let n: string = 'Guest'+Math.floor(Math.random() * Math.floor(1000));
|
||||||
|
if(store.get(n)) return newNick(socket, true);
|
||||||
|
else {
|
||||||
|
store.set(n, socket.id);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function chgNick(socket, nick) {
|
||||||
|
let rooms = Object.keys(socket.rooms);
|
||||||
|
for(let i=1;i<rooms.length;i++){
|
||||||
|
io.to(rooms[i]).emit('ALERT', socket.nick+' is now known as '+nick);
|
||||||
|
}
|
||||||
|
store.rm(socket.nick);
|
||||||
|
store.set(nick, socket.id);
|
||||||
|
socket.nick = nick;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function genToken(u: string){
|
||||||
|
return await JWT.sign({
|
||||||
|
username: u
|
||||||
|
}, jwkey, {
|
||||||
|
expiresIn: '1 week',
|
||||||
|
iat: true,
|
||||||
|
kid: false
|
||||||
|
});//set jwt
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validToken(t: any){
|
||||||
|
try {
|
||||||
|
let token = JWT.verify(t, jwkey);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
catch (err){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryDecode(t: any){
|
||||||
|
try {
|
||||||
|
return JWT.decode(t);
|
||||||
|
}
|
||||||
|
catch (err){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function parseCookie(c){
|
||||||
|
if(typeof(c) !== 'string' || !c.includes('=')) return {};
|
||||||
|
return Object.assign({[c.split('=')[0].trim()]:c.split('=')[1].split(';')[0].trim()}, await parseCookie(c.split(/;(.+)/)[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initAPI() {
|
||||||
|
app.get('/api/users/live', (req, res) => {
|
||||||
|
db.query('select username,title from user_meta where live=1 limit 10;').then((result) => {
|
||||||
|
res.send(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.get('/api/users/live/:num', (req, res) => {
|
||||||
|
if(req.params.num > 50) req.params.num = 50;
|
||||||
|
db.query('select username,title from user_meta where live=1 limit '+req.params.num+';').then((result) => {
|
||||||
|
res.send(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.post('/api/register', (req, res) => {
|
||||||
|
api.register(req.body.username, req.body.password, req.body.confirm).then( (result) => {
|
||||||
|
if(result[0]) return genToken(req.body.username).then((t) => {
|
||||||
|
res.cookie('Authorization', t);
|
||||||
|
res.send(result);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
res.send(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.post('/api/user/update', (req, res) => {
|
||||||
|
validToken(req.cookies.Authorization).then((t) => {
|
||||||
|
if(t) {
|
||||||
|
return api.update({name: t['username'],
|
||||||
|
title: "title" in req.body ? req.body.title : false,
|
||||||
|
bio: "bio" in req.body ? req.body.bio : false,
|
||||||
|
rec: "record" in req.body ? req.body.record : "NA"
|
||||||
|
}).then((r) => {
|
||||||
|
res.send(r);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.send('{"error":"invalid token"}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/*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) => {
|
||||||
|
validToken(req.cookies.Authorization).then((t) => {
|
||||||
|
if(t) {
|
||||||
|
return api.changepwd(t['username'], req.body.password, req.body.newpassword).then((r) => {
|
||||||
|
res.send(r);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.send('{"error":"invalid token"}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.post('/api/user/streamkey', (req, res) => {
|
||||||
|
validToken(req.cookies.Authorization).then((t) => {
|
||||||
|
if(t) {
|
||||||
|
api.changesk(t['username']).then((r) => {
|
||||||
|
res.send(r);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.send('{"error":"invalid token"}');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.post('/api/login', (req, res) => {
|
||||||
|
if(req.cookies.Authorization) validToken(req.cookies.Authorization).then((t) => {
|
||||||
|
if(t) {
|
||||||
|
if(t['exp'] - 86400 < Math.floor(Date.now() / 1000)){
|
||||||
|
return genToken(t['username']).then((t) => {
|
||||||
|
res.cookie('Authorization', t);
|
||||||
|
res.send('{"success":""}');
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.send('{"success":"already verified"}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.send('{"error":"invalid token"}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
else {
|
||||||
|
api.login(req.body.username, req.body.password).then((result) => {
|
||||||
|
if(!result){
|
||||||
|
genToken(req.body.username).then((t) => {
|
||||||
|
res.cookie('Authorization', t);
|
||||||
|
res.send('{"success":""}');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.send(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initSite(openReg) {
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
res.redirect(njkconf.rootredirect);
|
res.redirect(njkconf.rootredirect);
|
||||||
});
|
});
|
||||||
app.get('/about', (req, res) => {
|
app.get('/about', (req, res) => {
|
||||||
res.render('about.njk', njkconf);
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
|
res.render('about.njk', Object.assign({auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
|
}
|
||||||
|
else res.render('about.njk',njkconf);
|
||||||
});
|
});
|
||||||
app.get('/users', (req, res) => {
|
app.get('/users', (req, res) => {
|
||||||
db.query('select username from users').then((result) => {
|
db.query('select username from users').then((result) => {
|
||||||
res.render('list.njk', Object.assign({list: result}, njkconf));
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
|
res.render('list.njk', Object.assign({list: result}, {auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
|
}
|
||||||
|
else res.render('list.njk', Object.assign({list: result}, njkconf));
|
||||||
|
//res.render('list.njk', Object.assign({list: result}, njkconf));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
app.get('/users/live', (req, res) => {
|
app.get('/users/live', (req, res) => {
|
||||||
db.query('select username,title from user_meta where live=1;').then((result) => {
|
db.query('select username,title from user_meta where live=1;').then((result) => {
|
||||||
res.render('live.njk', Object.assign({list: result}, njkconf));
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
});
|
res.render('live.njk', Object.assign({list: result}, {auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
});
|
|
||||||
app.get('/users/*', (req, res) => {
|
|
||||||
db.query('select username,title,about from user_meta where username='+db.raw.escape(req.url.split('/')[2].toLowerCase())).then((result) => {
|
|
||||||
if(result[0]){
|
|
||||||
res.render('user.njk', Object.assign(result[0], njkconf));
|
|
||||||
}
|
}
|
||||||
else res.render('404.njk', njkconf);
|
else res.render('live.njk', Object.assign({list: result}, njkconf));
|
||||||
|
//res.render('live.njk', Object.assign({list: result}, njkconf));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
app.get('/vods/*', (req, res) => {
|
app.get('/users/:user', (req, res) => {
|
||||||
db.query('select username from user_meta where username='+db.raw.escape(req.url.split('/')[2].toLowerCase())).then((result) => {
|
db.query('select username,title,about from user_meta where username='+db.raw.escape(req.params.user)).then((result) => {
|
||||||
if(result[0]){
|
if(result[0]){
|
||||||
fs.readdir('./site/live/'+njkconf.user, {withFileTypes: true} , (err, files) => {
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
res.render('vods.njk', Object.assign({user: result[0].username, list: files.filter(fn => fn.name.endsWith('.mp4'))}, njkconf));
|
res.render('user.njk', Object.assign(result[0], {auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
|
}
|
||||||
|
else res.render('user.njk', Object.assign(result[0], njkconf));
|
||||||
|
//res.render('user.njk', Object.assign(result[0], njkconf));
|
||||||
|
}
|
||||||
|
else if(tryDecode(req.cookies.Authorization)) {
|
||||||
|
res.status(404).render('404.njk', Object.assign({auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
|
}
|
||||||
|
else res.status(404).render('404.njk', njkconf);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.get('/vods/:user', (req, res) => {
|
||||||
|
db.query('select username from user_meta where username='+db.raw.escape(req.params.user)).then((result) => {
|
||||||
|
if(result[0]){
|
||||||
|
fs.readdir('./site/live/'+result[0].username, {withFileTypes: true} , (err, files) => {
|
||||||
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
|
res.render('vods.njk', Object.assign({user: result[0].username, list: files.filter(fn => fn.name.endsWith('.mp4'))}, {auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
|
}
|
||||||
|
else res.render('vods.njk', Object.assign({user: result[0].username, list: files.filter(fn => fn.name.endsWith('.mp4'))}, njkconf));
|
||||||
|
//res.render('vods.njk', Object.assign({user: result[0].username, list: files.filter(fn => fn.name.endsWith('.mp4'))}, njkconf));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else res.render('404.njk', njkconf);
|
else if(tryDecode(req.cookies.Authorization)) {
|
||||||
|
res.status(404).render('404.njk', Object.assign({auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
|
}
|
||||||
|
else res.status(404).render('404.njk', njkconf);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
app.get('/login', (req, res) => {
|
||||||
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
|
res.redirect(njkconf.rootredirect);
|
||||||
|
}
|
||||||
|
else res.render('login.njk',njkconf);
|
||||||
|
});
|
||||||
app.get('/register', (req, res) => {
|
app.get('/register', (req, res) => {
|
||||||
res.render('registration.njk', njkconf);
|
if(tryDecode(req.cookies.Authorization) || !openReg) {
|
||||||
|
res.redirect(njkconf.rootredirect);
|
||||||
|
}
|
||||||
|
else res.render('registration.njk',njkconf);
|
||||||
});
|
});
|
||||||
app.get('/profile', (req, res) => {
|
app.get('/profile', (req, res) => {
|
||||||
res.render('profile.njk', njkconf);
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
|
res.render('profile.njk', Object.assign({auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
|
}
|
||||||
|
else res.redirect(njkconf.rootredirect);
|
||||||
});
|
});
|
||||||
app.get('/changepwd', (req, res) => {
|
app.get('/changepwd', (req, res) => {
|
||||||
res.render('changepwd.njk', njkconf);
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
});
|
res.render('changepwd.njk', Object.assign({auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
app.get('/changesk', (req, res) => {
|
}
|
||||||
res.render('changesk.njk', njkconf);
|
else res.redirect(njkconf.rootredirect);
|
||||||
});
|
});
|
||||||
app.get('/chat', (req, res) => {
|
app.get('/chat', (req, res) => {
|
||||||
res.render('chat.html', njkconf);
|
res.render('chat.html', njkconf);
|
||||||
});
|
});
|
||||||
app.get('/help', (req, res) => {
|
app.get('/help', (req, res) => {
|
||||||
res.render('help.njk', njkconf);
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
});
|
res.render('help.njk', Object.assign({auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
//api handlers
|
}
|
||||||
app.post('/api/register', (req, res) => {
|
else res.render('help.njk',njkconf);
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initChat(ircconf: any) {
|
||||||
//irc peering
|
//irc peering
|
||||||
if(ircconf.enable){
|
if(ircconf.enable){
|
||||||
await irc.connect({
|
await irc.connect({
|
||||||
|
@ -190,26 +402,6 @@ async function init(satyr: any, port: number, ircconf: any){
|
||||||
else socket.emit('ALERT', 'Not authorized to do that.');
|
else socket.emit('ALERT', 'Not authorized to do that.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
server.listen(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function newNick(socket) {
|
|
||||||
//i just realized how shitty of an idea this is
|
|
||||||
let n: string = 'Guest'+Math.floor(Math.random() * Math.floor(1000));
|
|
||||||
if(store.get(n)) return newNick(socket);
|
|
||||||
else {
|
|
||||||
store.set(n, socket.id);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function chgNick(socket, nick) {
|
|
||||||
let rooms = Object.keys(socket.rooms);
|
|
||||||
for(let i=1;i<rooms.length;i++){
|
|
||||||
io.to(rooms[i]).emit('ALERT', socket.nick+' is now known as '+nick);
|
|
||||||
}
|
|
||||||
store.rm(socket.nick);
|
|
||||||
store.set(nick, socket.id);
|
|
||||||
socket.nick = nick;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { init };
|
export { init };
|
|
@ -77,7 +77,7 @@ function init (mediaconfig: any, satyrconfig: any) {
|
||||||
if(app === satyrconfig.privateEndpoint) {
|
if(app === satyrconfig.privateEndpoint) {
|
||||||
db.query('update user_meta,users set user_meta.live=false where users.stream_key='+db.raw.escape(key));
|
db.query('update user_meta,users set user_meta.live=false where users.stream_key='+db.raw.escape(key));
|
||||||
db.query('select username from users where stream_key='+db.raw.escape(key)+' limit 1').then(async (results) => {
|
db.query('select username from users where stream_key='+db.raw.escape(key)+' limit 1').then(async (results) => {
|
||||||
keystore.rm(results[0].username);
|
if(results[0]) keystore.rm(results[0].username);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<body>
|
<body>
|
||||||
<div id="wrapper">
|
<div id="wrapper">
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<span style="float:left;"><h4><a href="/">{{ sitename }}</a> | <a href="/users">Users</a> <a href="/users/live">Live</a> <a href="/about">About</a></h4></span><span style="float:right;"><h4><a href="/help">Help</a> | <a href="/profile">Profile</a></h4></span>
|
<span style="float:left;"><h4><a href="/">{{ sitename }}</a> | <a href="/users">Users</a> <a href="/users/live">Live</a> <a href="/about">About</a></h4></span><span style="float:right;"><h4><a href="/help">Help</a> | {% if auth.is %}<a href="/profile">{{ auth.name }}{% else %}<a href="/login">Log In{% endif %}</a></h4></span>
|
||||||
</div>
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
{% extends "base.njk" %}
|
{% extends "base.njk" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h3>Change your password on {{ sitename }}</h3><span style="font-size: small;">Not registered yet? Sign up <a href="/register">here</a>.</br> Update your <a href="/profile">profile</a> or <a href="/changesk">stream key</a>.</span>
|
<h3>Change your password on {{ sitename }}</h3>
|
||||||
<p></p>
|
<p></p>
|
||||||
<form action="/api/user/password" method="POST" target="responseFrame">
|
<form action="/api/user/password" method="POST" target="responseFrame">
|
||||||
Username: </br><input type="text" name="username" style="min-width: 300px" placeholder="e.g. lain"/></br>
|
Old Password: </br><input type="password" name="password" style="min-width: 300px"/></br>
|
||||||
Password: </br><input type="password" name="password" style="min-width: 300px"/></br>
|
New Password: </br><input type="password" name="newpassword" style="min-width: 300px"/></br></br>
|
||||||
New Password: </br><input type="password" name="newpassword" style="min-width: 300px"/></br>
|
|
||||||
<input type="submit" value="Submit">
|
<input type="submit" value="Submit">
|
||||||
</form>
|
</form>
|
||||||
<iframe name="responseFrame" border="0" frameborder="0" style="display: inline;"></iframe>
|
<iframe name="responseFrame" border="0" frameborder="0" style="display: inline;"></iframe>
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
{% extends "base.njk" %}
|
|
||||||
{% block content %}
|
|
||||||
<h3>Get a new stream key on {{ sitename }}</h3><span style="font-size: small;">Not registered yet? Sign up <a href="/register">here</a>.</br> Update your <a href="/profile">profile</a> or <a href="/changepwd">password</a>.</span>
|
|
||||||
<p></p>
|
|
||||||
<form action="/api/user/streamkey" method="POST" target="responseFrame">
|
|
||||||
Username: </br><input type="text" name="username" style="min-width: 300px" placeholder="e.g. lain"/></br>
|
|
||||||
Password: </br><input type="password" name="password" style="min-width: 300px"/></br>
|
|
||||||
<input type="submit" value="Submit">
|
|
||||||
</form>
|
|
||||||
<iframe name="responseFrame" border="0" frameborder="0" style="display: inline;"></iframe>
|
|
||||||
{% endblock %}
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
{% extends "base.njk" %}
|
||||||
|
{% block content %}
|
||||||
|
<h3>Log in to {{ sitename }}</h3><span style="font-size: small;">Not registered yet? Sign up <a href="/register">here</a>.</br></br></span>
|
||||||
|
<form action="/api/login" method="POST" target="responseFrame">
|
||||||
|
Username: </br><input type="text" name="username" style="min-width: 300px" placeholder="e.g. lain"/></br>
|
||||||
|
Password: </br><input type="password" name="password" style="min-width: 300px"/></br>
|
||||||
|
<input type="submit" value="Submit">
|
||||||
|
</form>
|
||||||
|
<iframe name="responseFrame" border="0" frameborder="0" style="display: inline;"></iframe>
|
||||||
|
{% endblock %}
|
|
@ -1,14 +1,15 @@
|
||||||
{% extends "base.njk" %}
|
{% extends "base.njk" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h3>Update your profile on {{ sitename }}</h3><span style="font-size: small;">Not registered yet? Sign up <a href="/register">here</a>.</br> Change your <a href="/changepwd">password</a> or <a href="/changesk">stream key</a>.</span>
|
<h3>Update your profile on {{ sitename }}</h3><span style="font-size: small;">Or, change your <a href="/changepwd">password</a>.</span>
|
||||||
<p></p>
|
<p></p>
|
||||||
<form action="/api/user" method="POST" target="responseFrame">
|
<form action="/api/user/update" method="POST" target="responseFrame">
|
||||||
Username: </br><input type="text" name="username" style="min-width: 300px" placeholder="e.g. lain"/></br>
|
|
||||||
Password: </br><input type="password" name="password" style="min-width: 300px"/></br>
|
|
||||||
Stream Title: </br><input type="text" name="title" style="min-width: 300px"/></br>
|
Stream Title: </br><input type="text" name="title" style="min-width: 300px"/></br>
|
||||||
Bio: </br><input type="text" name="bio" style="min-width: 300px; min-height: 150px;"/></br>
|
Bio: </br><input type="text" name="bio" style="min-width: 300px; min-height: 150px;"/></br>
|
||||||
Record VODs: <input type="checkbox" name="record" value="true"></br>
|
Record VODs: <input type="radio" name="record" value="true"> Yes<input type="radio" name="record" value="false" /> No</br></br>
|
||||||
<input type="submit" value="Submit">
|
<input type="submit" value="Update Profile">
|
||||||
|
</form></br>
|
||||||
|
<form action="/api/user/streamkey" method="POST" target="responseFrame">
|
||||||
|
<input type="submit" value="Request New Stream Key">
|
||||||
</form>
|
</form>
|
||||||
<iframe name="responseFrame" border="0" frameborder="0" style="display: inline;"></iframe>
|
<iframe name="responseFrame" border="0" frameborder="0" style="display: inline;"></iframe>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -1,17 +1,19 @@
|
||||||
{% extends "base.njk" %}
|
{% extends "base.njk" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="jscontainer" style="height: 100%;">
|
<h3>Register on {{ sitename }}</h3><span style="font-size: small;">Already registered? Log in <a href="/login">here</a>.</br></br></span>
|
||||||
<div id="jschild" style="width: 50%;height: 100%;text-align: left;margin: 20px;">
|
<!--<div id="jscontainer" style="height: 100%;">
|
||||||
|
<div id="jschild" style="width: 50%;height: 100%;text-align: left;margin: 20px;">-->
|
||||||
<form action="/api/register" method="POST" target="responseFrame">
|
<form action="/api/register" method="POST" target="responseFrame">
|
||||||
Username: </br><input type="text" name="username" style="min-width: 300px" placeholder="e.g. lain"/></br>
|
Username: </br><input type="text" name="username" style="min-width: 300px" placeholder="e.g. lain"/></br>
|
||||||
Password: </br><input type="password" name="password" style="min-width: 300px"/></br>
|
Password: </br><input type="password" name="password" style="min-width: 300px"/></br>
|
||||||
Confirm: </br><input type="password" name="confirm" style="min-width: 300px"/></br>
|
Confirm: </br><input type="password" name="confirm" style="min-width: 300px"/></br></br>
|
||||||
<input type="submit" value="Submit">
|
<input type="submit" value="Submit">
|
||||||
</form>
|
</form></br>
|
||||||
|
|
||||||
|
<!--</div>
|
||||||
|
<div id="jschild" style="width: 50%;height: 100%;text-align: left;margin: 20px;">-->
|
||||||
|
{% include "tos.html" %}</br>
|
||||||
<iframe name="responseFrame" border="0" frameborder="0" style="display: inline;"></iframe>
|
<iframe name="responseFrame" border="0" frameborder="0" style="display: inline;"></iframe>
|
||||||
</div>
|
<!--</div>
|
||||||
<div id="jschild" style="width: 50%;height: 100%;text-align: left;margin: 20px;">
|
</div>-->
|
||||||
{% include "tos.html" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
Reference in New Issue