Big Refactor
Stop using config and toml as dependencies Stop passing around config variables through function calls Add config.ts and pull the values you need directly in the files Remove irc.js for incoming new IRC solution Rename controller to index because that was stupid Minor git bullshit with the config folder Change to yaml as a config formatmerge-requests/7/head
parent
681802dbaf
commit
f7733b9507
|
@ -1,8 +1,6 @@
|
||||||
node_modules
|
node_modules
|
||||||
site/live
|
site/live
|
||||||
config/local.toml
|
config/**/*
|
||||||
config/jwt.pem
|
!config/.gitkeep
|
||||||
config/generated.toml
|
|
||||||
config/bans.db
|
|
||||||
install/db_setup.sql
|
install/db_setup.sql
|
||||||
build/**
|
build/**
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
#DO NOT EDIT THIS FILE
|
|
||||||
#ALL CHANGES SHOULD GO IN LOCAL.TOML
|
|
||||||
[bcrypt]
|
|
||||||
saltRounds = 12
|
|
||||||
|
|
||||||
[satyr]
|
|
||||||
name = ''
|
|
||||||
domain = ''
|
|
||||||
registration = false
|
|
||||||
restrictedNames = ['live']
|
|
||||||
rootredirect = '/users/live'
|
|
||||||
|
|
||||||
[ircd]
|
|
||||||
enable = false
|
|
||||||
port = 6667
|
|
||||||
sid = ''
|
|
||||||
server = ''
|
|
||||||
pass = ''
|
|
||||||
vhost = 'web.satyr.net'
|
|
||||||
|
|
||||||
[database]
|
|
||||||
host = 'localhost'
|
|
||||||
user = 'satyr'
|
|
||||||
password = ''
|
|
||||||
database = 'satyr_db'
|
|
||||||
connectionLimit = '50'
|
|
||||||
connectionTimeout = '1000'
|
|
||||||
insecureAuth = false
|
|
||||||
debug = false
|
|
||||||
|
|
||||||
[server]
|
|
||||||
logs = 0
|
|
||||||
api = false
|
|
||||||
api_user = false
|
|
||||||
api_pass = false
|
|
||||||
|
|
||||||
[server.rtmp]
|
|
||||||
port = 1935
|
|
||||||
chunk_size = 6000
|
|
||||||
gop_cache = true
|
|
||||||
ping = 30
|
|
||||||
ping_timeout = 60
|
|
||||||
|
|
||||||
[server.http]
|
|
||||||
hsts = false
|
|
||||||
directory = './site'
|
|
||||||
port = 8000
|
|
||||||
|
|
||||||
[media]
|
|
||||||
record = false
|
|
||||||
publicEndpoint = 'live'
|
|
||||||
privateEndpoint = 'stream'
|
|
||||||
ffmpeg = ''
|
|
||||||
|
|
||||||
[transcode]
|
|
||||||
adapative = false
|
|
||||||
variants = 3
|
|
||||||
format = 'dash'
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
satyr:
|
||||||
|
name: '<iname>'
|
||||||
|
domain: '<domain>'
|
||||||
|
email: '<email>'
|
||||||
|
registration: false
|
||||||
|
|
||||||
|
media:
|
||||||
|
record: false
|
||||||
|
ffmpeg: '<ffmpeg>'
|
||||||
|
|
||||||
|
http:
|
||||||
|
# uncomment to set HSTS when SSL is ready
|
||||||
|
#hsts: true
|
||||||
|
|
||||||
|
database:
|
||||||
|
user: '<dbuser>'
|
||||||
|
password: '<dbpass>'
|
||||||
|
database: '<dbname>'
|
||||||
|
host: '<dbhost>'
|
||||||
|
|
||||||
|
transcode:
|
||||||
|
adaptive: false
|
||||||
|
format: dash
|
||||||
|
variants: 3
|
|
@ -38,8 +38,8 @@ dbclient="${dbclient:='*'}"
|
||||||
else
|
else
|
||||||
dbclient="localhost"
|
dbclient="localhost"
|
||||||
fi
|
fi
|
||||||
sed -e "s#<iname>#$name#g" -e "s#<domain>#$domain#g" -e "s#<ffmpeg>#$ffmpeg#g" -e "s#<dbuser>#$dbuser#g" -e "s#<dbname>#$dbname#g" -e "s#<dbpass>#$dbpass#g" -e "s#<dbhost>#$dbhost#g" -e "s#<email>#$email#g" install/template.local.toml > config/generated.toml
|
sed -e "s#<iname>#$name#g" -e "s#<domain>#$domain#g" -e "s#<ffmpeg>#$ffmpeg#g" -e "s#<dbuser>#$dbuser#g" -e "s#<dbname>#$dbname#g" -e "s#<dbpass>#$dbpass#g" -e "s#<dbhost>#$dbhost#g" -e "s#<email>#$email#g" install/config.example.yml > config/generated.yml
|
||||||
sed -e "s#<dbuser>#$dbuser#g" -e "s#<dbname>#$dbname#g" -e "s#<dbpass>#$dbpass#g" -e "s#<dbhost>#$dbhost#g" -e "s#<dbclient>#$dbclient#g" install/db_template.sql > install/db_setup.sql
|
sed -e "s#<dbuser>#$dbuser#g" -e "s#<dbname>#$dbname#g" -e "s#<dbpass>#$dbpass#g" -e "s#<dbhost>#$dbhost#g" -e "s#<dbclient>#$dbclient#g" install/db_template.sql > install/db_setup.sql
|
||||||
echo "A setup script for the database has been generated at install/db_setup.sql. Please run it by connecting to your database software and executing 'source install/db_setup.sql;''"
|
echo "A setup script for the database has been generated at install/db_setup.sql. Please run it by connecting to your database software and executing 'source install/db_setup.sql;''"
|
||||||
echo "A default configuration file has been generated at config/generated.toml"
|
echo "A default configuration file has been generated at config/generated.yml"
|
||||||
echo "If everything looks fine, move it to config/local.toml and start your instance."
|
echo "If everything looks fine, move it to config/config.yml and start your instance."
|
|
@ -1,19 +0,0 @@
|
||||||
[satyr]
|
|
||||||
name = '<iname>'
|
|
||||||
domain = '<domain>'
|
|
||||||
email = '<email>'
|
|
||||||
registration = false
|
|
||||||
|
|
||||||
[media]
|
|
||||||
record = false
|
|
||||||
ffmpeg = '<ffmpeg>'
|
|
||||||
|
|
||||||
[server.http]
|
|
||||||
# uncomment to set HSTS when SSL is enabled
|
|
||||||
# hsts = true
|
|
||||||
|
|
||||||
[database]
|
|
||||||
user = '<dbuser>'
|
|
||||||
password = '<dbpass>'
|
|
||||||
database = '<dbname>'
|
|
||||||
host = '<dbhost>'
|
|
|
@ -82,6 +82,11 @@
|
||||||
"readable-stream": "^2.0.6"
|
"readable-stream": "^2.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"arg": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg=="
|
||||||
|
},
|
||||||
"arr-diff": {
|
"arr-diff": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
|
||||||
|
@ -342,6 +347,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"buffer-from": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||||
|
},
|
||||||
"bytes": {
|
"bytes": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
||||||
|
@ -644,6 +654,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
|
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
|
||||||
},
|
},
|
||||||
|
"diff": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q=="
|
||||||
|
},
|
||||||
"dirty": {
|
"dirty": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/dirty/-/dirty-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/dirty/-/dirty-1.1.0.tgz",
|
||||||
|
@ -1910,6 +1925,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||||
},
|
},
|
||||||
|
"make-error": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
|
||||||
|
"integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g=="
|
||||||
|
},
|
||||||
"map-cache": {
|
"map-cache": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
|
||||||
|
@ -2292,6 +2312,11 @@
|
||||||
"os-tmpdir": "^1.0.0"
|
"os-tmpdir": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"parse-yaml": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-yaml/-/parse-yaml-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-tLfs2QiziUPFTA4nNrv2rrC0CnHDIF2o2m5TCgNss/E0asI0ltVjBcNKhcd/8vteZa8xKV5RGfD0ZFFlECMCqQ=="
|
||||||
|
},
|
||||||
"parseqs": {
|
"parseqs": {
|
||||||
"version": "0.0.5",
|
"version": "0.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
|
||||||
|
@ -2868,6 +2893,22 @@
|
||||||
"urix": "^0.1.0"
|
"urix": "^0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"source-map-support": {
|
||||||
|
"version": "0.5.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
|
||||||
|
"integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
|
||||||
|
"requires": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"source-map": "^0.6.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"source-map-url": {
|
"source-map-url": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
|
||||||
|
@ -3029,6 +3070,18 @@
|
||||||
"resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
|
||||||
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="
|
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="
|
||||||
},
|
},
|
||||||
|
"ts-node": {
|
||||||
|
"version": "8.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.5.4.tgz",
|
||||||
|
"integrity": "sha512-izbVCRV68EasEPQ8MSIGBNK9dc/4sYJJKYA+IarMQct1RtEot6Xp0bXuClsbUSnKpg50ho+aOAx8en5c+y4OFw==",
|
||||||
|
"requires": {
|
||||||
|
"arg": "^4.1.0",
|
||||||
|
"diff": "^4.0.1",
|
||||||
|
"make-error": "^1.1.1",
|
||||||
|
"source-map-support": "^0.5.6",
|
||||||
|
"yn": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"type-is": {
|
"type-is": {
|
||||||
"version": "1.6.18",
|
"version": "1.6.18",
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
|
@ -3201,6 +3254,11 @@
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
|
||||||
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
||||||
|
},
|
||||||
|
"yn": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"author": "knotteye",
|
"author": "knotteye",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "tsc && node build/controller.js",
|
"start": "ts-node src/index.ts",
|
||||||
"user": "node build/cli.js",
|
"user": "ts-node src/cli.ts",
|
||||||
"setup": "sh install/setup.sh"
|
"setup": "sh install/setup.sh"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -16,7 +16,6 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypt": "^3.0.6",
|
"bcrypt": "^3.0.6",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"config": "^3.2.2",
|
|
||||||
"cookie-parser": "^1.4.4",
|
"cookie-parser": "^1.4.4",
|
||||||
"dirty": "^1.1.0",
|
"dirty": "^1.1.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
|
@ -25,11 +24,12 @@
|
||||||
"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",
|
||||||
|
"parse-yaml": "^0.1.0",
|
||||||
"recursive-readdir": "^2.2.2",
|
"recursive-readdir": "^2.2.2",
|
||||||
"socket-anti-spam": "^2.0.0",
|
"socket-anti-spam": "^2.0.0",
|
||||||
"socket.io": "^2.3.0",
|
"socket.io": "^2.3.0",
|
||||||
"strftime": "^0.10.0",
|
"strftime": "^0.10.0",
|
||||||
"toml": "^3.0.0",
|
"ts-node": "^8.5.4",
|
||||||
"typescript": "^3.6.3"
|
"typescript": "^3.6.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
15
src/api.ts
15
src/api.ts
|
@ -1,17 +1,12 @@
|
||||||
import * as db from "./database"
|
import * as db from "./database"
|
||||||
import { unregisterUser } from "./irc";
|
import { config } from "./config";
|
||||||
|
|
||||||
var config: any;
|
|
||||||
function init(conf: object){
|
|
||||||
config = conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function register(name: string, password: string, confirm: string) {
|
async function register(name: string, password: string, confirm: string) {
|
||||||
if(!config.registration) return {"error":"registration disabled"};
|
if(!config['satyr']['registration']) return {"error":"registration disabled"};
|
||||||
if(name.includes(';') || name.includes(' ') || name.includes('\'')) return {"error":"illegal characters"};
|
if(name.includes(';') || name.includes(' ') || name.includes('\'')) return {"error":"illegal characters"};
|
||||||
if(password !== confirm) return {"error":"mismatched passwords"};
|
if(password !== confirm) return {"error":"mismatched passwords"};
|
||||||
for(let i=0;i<config.restrictedNames.length;i++){
|
for(let i=0;i<config['satyr']['restrictedNames'].length;i++){
|
||||||
if (name === config.restrictedNames[i]) return {"error":"restricted name"};
|
if (name === config['satyr']['restrictedNames'][i]) return {"error":"restricted name"};
|
||||||
}
|
}
|
||||||
let r: boolean = await db.addUser(name, password);
|
let r: boolean = await db.addUser(name, password);
|
||||||
if(r) {
|
if(r) {
|
||||||
|
@ -61,4 +56,4 @@ async function login(name: string, password: string){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { init, register, update, changepwd, changesk, login };
|
export { register, update, changepwd, changesk, login };
|
|
@ -1,8 +1,7 @@
|
||||||
import * as db from "./database"
|
import * as db from "./database"
|
||||||
import * as flags from "flags";
|
import * as flags from "flags";
|
||||||
import * as config from "config"
|
|
||||||
|
|
||||||
db.init(config.database, config.bcrypt);
|
db.init();
|
||||||
|
|
||||||
flags.defineString('adduser', '', 'User to add');
|
flags.defineString('adduser', '', 'User to add');
|
||||||
flags.defineString('rmuser', '', 'User to remove');
|
flags.defineString('rmuser', '', 'User to remove');
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import {parseAsYaml as parse} from "parse-yaml";
|
||||||
|
import {readFileSync as read} from "fs";
|
||||||
|
var localconfig: Object = parse(read('config/config.yml'));
|
||||||
|
const config: Object = {
|
||||||
|
crypto: Object.assign({
|
||||||
|
saltRounds: 12
|
||||||
|
}, localconfig['crypto']),
|
||||||
|
satyr: Object.assign({
|
||||||
|
name: '',
|
||||||
|
domain: '',
|
||||||
|
registration: false,
|
||||||
|
restrictedNames: [ 'live' ],
|
||||||
|
rootredirect: '/users/live',
|
||||||
|
version: process.env.npm_package_version,
|
||||||
|
}, localconfig['satyr']),
|
||||||
|
ircd: Object.assign({
|
||||||
|
port: 6667,
|
||||||
|
}, localconfig['ircd']),
|
||||||
|
database: Object.assign({
|
||||||
|
host: 'localhost',
|
||||||
|
user: 'satyr',
|
||||||
|
password: '',
|
||||||
|
database: 'satyr_db',
|
||||||
|
connectionLimit: '50',
|
||||||
|
connectionTimeout: '1000',
|
||||||
|
insecureAuth: false,
|
||||||
|
debug: false }, localconfig['database']),
|
||||||
|
rtmp: Object.assign({
|
||||||
|
port: 1935,
|
||||||
|
chunk_size: 6000,
|
||||||
|
gop_cache: true,
|
||||||
|
ping: 30,
|
||||||
|
ping_timeout: 60 }, localconfig['rtmp']),
|
||||||
|
http: Object.assign({
|
||||||
|
hsts: false, directory: './site', port: 8000
|
||||||
|
}, localconfig['http']),
|
||||||
|
media: Object.assign({
|
||||||
|
record: false,
|
||||||
|
publicEndpoint: 'live',
|
||||||
|
privateEndpoint: 'stream',
|
||||||
|
ffmpeg: ''
|
||||||
|
}, localconfig['media']),
|
||||||
|
transcode: Object.assign({
|
||||||
|
adapative: false,
|
||||||
|
variants: 3,
|
||||||
|
format: 'dash'
|
||||||
|
}, localconfig['transcode'])
|
||||||
|
};
|
||||||
|
export { config };
|
|
@ -1,67 +0,0 @@
|
||||||
import * as mediaserver from "./server";
|
|
||||||
import * as db from "./database";
|
|
||||||
import * as api from "./api";
|
|
||||||
import * as http from "./http";
|
|
||||||
import * as cleanup from "./cleanup";
|
|
||||||
import * as config from "config";
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
const dbcfg: object = config.database;
|
|
||||||
const bcryptcfg: object = config.bcrypt;
|
|
||||||
const satyr: object = {
|
|
||||||
privateEndpoint: config.media.privateEndpoint,
|
|
||||||
publicEndpoint: config.media.publicEndpoint,
|
|
||||||
record: config.media.record,
|
|
||||||
registration: config.satyr.registration,
|
|
||||||
webFormat: config.satyr.webFormat,
|
|
||||||
restrictedNames: config.satyr.restrictedNames,
|
|
||||||
name: config.satyr.name,
|
|
||||||
domain: config.satyr.domain,
|
|
||||||
email: config.satyr.email,
|
|
||||||
rootredirect: config.satyr.rootredirect,
|
|
||||||
version: process.env.npm_package_version,
|
|
||||||
directory: config.server.http.directory,
|
|
||||||
ffmpeg: config.media.ffmpeg
|
|
||||||
};
|
|
||||||
const nms: object = {
|
|
||||||
logType: config.server.logs,
|
|
||||||
rtmp: {
|
|
||||||
port: config.server.rtmp.port,
|
|
||||||
chunk_size: config.server.rtmp.chunk_size,
|
|
||||||
gop_cache: config.server.rtmp.gop_cache,
|
|
||||||
ping: config.server.rtmp.ping,
|
|
||||||
ping_timeout: config.server.rtmp.ping_timeout,
|
|
||||||
},
|
|
||||||
/*http: {
|
|
||||||
port: config.server.http.port + 1,
|
|
||||||
mediaroot: config.server.http.directory,
|
|
||||||
allow_origin: config.server.http.allow_origin
|
|
||||||
},
|
|
||||||
trans: {
|
|
||||||
ffmpeg: config.media.ffmpeg,
|
|
||||||
tasks: [
|
|
||||||
{
|
|
||||||
app: config.media.publicEndpoint,
|
|
||||||
hls: config.transcode.hls,
|
|
||||||
hlsFlags: config.transcode.hlsFlags,
|
|
||||||
dash: config.transcode.dash,
|
|
||||||
dashFlags: config.transcode.dashFlags
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},*/
|
|
||||||
auth: {
|
|
||||||
api: config.server.api,
|
|
||||||
api_user: config.server.api_user,
|
|
||||||
api_pass: config.server.api_pass
|
|
||||||
},
|
|
||||||
transcode: config.transcode
|
|
||||||
};
|
|
||||||
db.init(dbcfg, bcryptcfg);
|
|
||||||
await cleanup.init();
|
|
||||||
api.init(satyr);
|
|
||||||
http.init(satyr, config.server.http, config.ircd);
|
|
||||||
mediaserver.init(nms, satyr);
|
|
||||||
console.log(`Satyr v${process.env.npm_package_version} ready`);
|
|
||||||
}
|
|
||||||
run();
|
|
||||||
export { run };
|
|
|
@ -1,20 +1,21 @@
|
||||||
import * as mysql from "mysql";
|
import * as mysql from "mysql";
|
||||||
import * as bcrypt from "bcrypt";
|
import * as bcrypt from "bcrypt";
|
||||||
import * as crypto from "crypto";
|
import * as crypto from "crypto";
|
||||||
|
import { config } from "./config";
|
||||||
import { resolve } from "url";
|
import { resolve } from "url";
|
||||||
var raw: any;
|
var raw;
|
||||||
var cryptoconfig: any;
|
var cryptoconfig: Object;
|
||||||
|
|
||||||
function init (db: object, bcrypt: object){
|
function init (){
|
||||||
raw = mysql.createPool(db);
|
raw = mysql.createPool(config['database']);
|
||||||
cryptoconfig = bcrypt;
|
cryptoconfig = config['crypto'];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addUser(name: string, password: string){
|
async function addUser(name: string, password: string){
|
||||||
//does not respect registration setting in config
|
//does not respect registration setting in config
|
||||||
if(password === '') return false;
|
if(password === '') return false;
|
||||||
let key: string = await genKey();
|
let key: string = await genKey();
|
||||||
let hash: string = await bcrypt.hash(password, cryptoconfig.saltRounds);
|
let hash: string = await bcrypt.hash(password, cryptoconfig['saltRounds']);
|
||||||
let dupe = await query('select * from users where username='+raw.escape(name));
|
let dupe = await query('select * from users where username='+raw.escape(name));
|
||||||
if(dupe[0]) return false;
|
if(dupe[0]) return false;
|
||||||
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 users (username, password_hash, stream_key, record_flag) VALUES ('+raw.escape(name)+', '+raw.escape(hash)+', '+raw.escape(key)+', 0)');
|
||||||
|
@ -54,7 +55,7 @@ async function validatePassword(username: string, password: string){
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hash(pwd){
|
async function hash(pwd){
|
||||||
return await bcrypt.hash(pwd, cryptoconfig.saltRounds);
|
return await bcrypt.hash(pwd, cryptoconfig['saltRounds']);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { query, raw, init, addUser, rmUser, validatePassword, hash, genKey };
|
export { query, raw, init, addUser, rmUser, validatePassword, hash, genKey };
|
56
src/http.ts
56
src/http.ts
|
@ -8,7 +8,7 @@ import * as dirty from "dirty";
|
||||||
import * as socketSpam from "socket-anti-spam";
|
import * as socketSpam from "socket-anti-spam";
|
||||||
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 { config } from "./config";
|
||||||
import { readdir, readFileSync, writeFileSync } from "fs";
|
import { readdir, readFileSync, writeFileSync } from "fs";
|
||||||
import { JWT, JWK } from "jose";
|
import { JWT, JWK } from "jose";
|
||||||
import { strict } from "assert";
|
import { strict } from "assert";
|
||||||
|
@ -20,10 +20,10 @@ const server = http.createServer(app);
|
||||||
const io = socketio(server);
|
const io = socketio(server);
|
||||||
const store = dirty();
|
const store = dirty();
|
||||||
var banlist;
|
var banlist;
|
||||||
var ircconf;
|
|
||||||
var jwkey;
|
var jwkey;
|
||||||
try{
|
try{
|
||||||
jwkey = JWK.asKey(readFileSync('./config/jwt.pem'));
|
jwkey = JWK.asKey(readFileSync('./config/jwt.pem'));
|
||||||
|
console.log('Found key for JWT signing.');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("No key found for JWT signing, generating one now.");
|
console.log("No key found for JWT signing, generating one now.");
|
||||||
jwkey = JWK.generateSync('RSA', 2048, { use: 'sig' });
|
jwkey = JWK.generateSync('RSA', 2048, { use: 'sig' });
|
||||||
|
@ -31,24 +31,23 @@ try{
|
||||||
}
|
}
|
||||||
var njkconf;
|
var njkconf;
|
||||||
|
|
||||||
async function init(satyr: any, http: object, irc: any){
|
async function init(){
|
||||||
ircconf = irc;
|
|
||||||
njk.configure('templates', {
|
njk.configure('templates', {
|
||||||
autoescape : true,
|
autoescape : true,
|
||||||
express : app,
|
express : app,
|
||||||
watch : false
|
watch : false
|
||||||
});
|
});
|
||||||
njkconf ={
|
njkconf = {
|
||||||
sitename: satyr.name,
|
sitename: config['satyr']['name'],
|
||||||
domain: satyr.domain,
|
domain: config['satyr']['domain'],
|
||||||
email: satyr.email,
|
email: config['satyr']['email'],
|
||||||
rootredirect: satyr.rootredirect,
|
rootredirect: config['satyr']['rootredirect'],
|
||||||
version: satyr.version
|
version: config['satyr']['version']
|
||||||
};
|
};
|
||||||
app.use(cookies());
|
app.use(cookies());
|
||||||
app.use(bodyparser.json());
|
app.use(bodyparser.json());
|
||||||
app.use(bodyparser.urlencoded({ extended: true }));
|
app.use(bodyparser.urlencoded({ extended: true }));
|
||||||
if(http['hsts']){
|
if(config['http']['hsts']){
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
res.append('Strict-Transport-Security', 'max-age=5184000');
|
res.append('Strict-Transport-Security', 'max-age=5184000');
|
||||||
next();
|
next();
|
||||||
|
@ -56,11 +55,11 @@ async function init(satyr: any, http: object, irc: any){
|
||||||
}
|
}
|
||||||
app.disable('x-powered-by');
|
app.disable('x-powered-by');
|
||||||
//site handlers
|
//site handlers
|
||||||
await initSite(satyr.registration);
|
await initSite(config['satyr']['registration']);
|
||||||
//api handlers
|
//api handlers
|
||||||
await initAPI();
|
await initAPI();
|
||||||
//static files if nothing else matches first
|
//static files if nothing else matches first
|
||||||
app.use(express.static(satyr.directory));
|
app.use(express.static(config['http']['directory']));
|
||||||
//404 Handler
|
//404 Handler
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
if(tryDecode(req.cookies.Authorization)) {
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
|
@ -70,7 +69,7 @@ async function init(satyr: any, http: object, irc: any){
|
||||||
//res.status(404).render('404.njk', njkconf);
|
//res.status(404).render('404.njk', njkconf);
|
||||||
});
|
});
|
||||||
banlist = new dirty('./config/bans.db').on('load', () => {initChat()});
|
banlist = new dirty('./config/bans.db').on('load', () => {initChat()});
|
||||||
server.listen(http['port']);
|
server.listen(config['http']['port']);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function newNick(socket, skip?: boolean, i?: number) {
|
async function newNick(socket, skip?: boolean, i?: number) {
|
||||||
|
@ -336,23 +335,17 @@ async function initSite(openReg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initChat() {
|
async function initChat() {
|
||||||
//irc peering
|
//set a cookie to request same nick
|
||||||
if(ircconf.enable){
|
//apply same nick if the request comes from the same ip
|
||||||
await irc.connect({
|
//structure of entry in store should be:
|
||||||
port: ircconf.port,
|
// knotteye: {
|
||||||
sid: ircconf.sid,
|
// ip: "127.0.0.1",
|
||||||
pass: ircconf.pass,
|
// id: ["aklsjdnaksj", "asjdnaksjnd", "aksjdnkajs"]
|
||||||
server: ircconf.server,
|
//}
|
||||||
vhost: ircconf.vhost
|
|
||||||
});
|
|
||||||
irc.events.on('message', (nick, channel, msg) => {
|
|
||||||
io.to(channel).emit('MSG', {nick: nick, msg: msg});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//socket.io chat logic
|
//socket.io chat logic
|
||||||
io.on('connection', async (socket) => {
|
io.on('connection', async (socket) => {
|
||||||
socket.nick = await newNick(socket);
|
socket.nick = await newNick(socket);
|
||||||
if(ircconf.enable) irc.registerUser(socket.nick);
|
|
||||||
socket.on('JOINROOM', async (data) => {
|
socket.on('JOINROOM', async (data) => {
|
||||||
let t: any = await db.query('select username from users where username='+db.raw.escape(data));
|
let t: any = await db.query('select username from users where username='+db.raw.escape(data));
|
||||||
if(t[0]){
|
if(t[0]){
|
||||||
|
@ -367,7 +360,6 @@ async function initChat() {
|
||||||
}
|
}
|
||||||
socket.join(data);
|
socket.join(data);
|
||||||
io.to(data).emit('JOINED', {nick: socket.nick});
|
io.to(data).emit('JOINED', {nick: socket.nick});
|
||||||
if(ircconf.enable) irc.join(socket.nick, data);
|
|
||||||
}
|
}
|
||||||
else socket.emit('ALERT', 'Room does not exist');
|
else socket.emit('ALERT', 'Room does not exist');
|
||||||
});
|
});
|
||||||
|
@ -388,16 +380,13 @@ async function initChat() {
|
||||||
});
|
});
|
||||||
socket.on('LEAVEROOM', (data) => {
|
socket.on('LEAVEROOM', (data) => {
|
||||||
socket.leave(data);
|
socket.leave(data);
|
||||||
if(ircconf.enable) irc.part(socket.nick, data);
|
|
||||||
io.to(data).emit('LEFT', {nick: socket.nick});
|
io.to(data).emit('LEFT', {nick: socket.nick});
|
||||||
});
|
});
|
||||||
socket.on('disconnecting', (reason) => {
|
socket.on('disconnecting', (reason) => {
|
||||||
let rooms = Object.keys(socket.rooms);
|
let rooms = Object.keys(socket.rooms);
|
||||||
for(let i=1;i<rooms.length;i++){
|
for(let i=1;i<rooms.length;i++){
|
||||||
if(ircconf.enable) irc.part(socket.nick, rooms[i]);
|
|
||||||
io.to(rooms[i]).emit('ALERT', socket.nick+' disconnected');
|
io.to(rooms[i]).emit('ALERT', socket.nick+' disconnected');
|
||||||
}
|
}
|
||||||
if(ircconf.enable) irc.unregisterUser(socket.nick);
|
|
||||||
store.rm(socket.nick);
|
store.rm(socket.nick);
|
||||||
});
|
});
|
||||||
socket.on('NICK', async (data) => {
|
socket.on('NICK', async (data) => {
|
||||||
|
@ -424,7 +413,6 @@ async function initChat() {
|
||||||
socket.on('MSG', (data) => {
|
socket.on('MSG', (data) => {
|
||||||
if(data.msg === "" || !data.msg.replace(/\s/g, '').length) return;
|
if(data.msg === "" || !data.msg.replace(/\s/g, '').length) return;
|
||||||
io.to(data.room).emit('MSG', {nick: socket.nick, msg: data.msg});
|
io.to(data.room).emit('MSG', {nick: socket.nick, msg: data.msg});
|
||||||
if(ircconf.enable) irc.send(socket.nick, data.room, data.msg);
|
|
||||||
});
|
});
|
||||||
socket.on('KICK', (data) => {
|
socket.on('KICK', (data) => {
|
||||||
if(socket.nick === data.room){
|
if(socket.nick === data.room){
|
||||||
|
@ -491,10 +479,8 @@ async function initChat() {
|
||||||
socketAS.event.on('ban', (socket) => {
|
socketAS.event.on('ban', (socket) => {
|
||||||
let rooms = Object.keys(socket.rooms);
|
let rooms = Object.keys(socket.rooms);
|
||||||
for(let i=1;i<rooms.length;i++){
|
for(let i=1;i<rooms.length;i++){
|
||||||
if(ircconf.enable) irc.part(socket.nick, rooms[i]);
|
|
||||||
io.to(rooms[i]).emit('ALERT', socket.nick+' was banned.');
|
io.to(rooms[i]).emit('ALERT', socket.nick+' was banned.');
|
||||||
}
|
}
|
||||||
if(ircconf.enable) irc.unregisterUser(socket.nick);
|
|
||||||
store.rm(socket.nick);
|
store.rm(socket.nick);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import * as mediaserver from "./server";
|
||||||
|
import * as db from "./database";
|
||||||
|
import * as http from "./http";
|
||||||
|
import * as cleanup from "./cleanup";
|
||||||
|
import { config } from "./config";
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
await db.init();
|
||||||
|
await cleanup.init();
|
||||||
|
await http.init();
|
||||||
|
await mediaserver.init();
|
||||||
|
console.log(`Satyr v${config['satyr']['version']} ready`);
|
||||||
|
}
|
||||||
|
run();
|
||||||
|
export { run };
|
212
src/irc.js
212
src/irc.js
|
@ -1,212 +0,0 @@
|
||||||
// written by crushv <nik@telekem.net>
|
|
||||||
// thanks nikki
|
|
||||||
|
|
||||||
const net = require('net')
|
|
||||||
const EventEmitter = require('events')
|
|
||||||
|
|
||||||
const socket = new net.Socket()
|
|
||||||
const emitter = new EventEmitter()
|
|
||||||
|
|
||||||
socket.setEncoding('utf8')
|
|
||||||
|
|
||||||
socket.on('error', console.error)
|
|
||||||
|
|
||||||
function m (text) {
|
|
||||||
console.log('> ' + text)
|
|
||||||
socket.write(text + '\r\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
var config
|
|
||||||
|
|
||||||
socket.once('connect', async () => {
|
|
||||||
console.log('Connected')
|
|
||||||
m(`PASS ${config.pass} TS 6 :${config.sid}`)
|
|
||||||
m('CAPAB QS ENCAP EX IE SAVE EUID')
|
|
||||||
m(`SERVER ${config.server} 1 satyr`)
|
|
||||||
})
|
|
||||||
|
|
||||||
function parseLine (l) {
|
|
||||||
const colIndex = l.lastIndexOf(':')
|
|
||||||
if (colIndex > -1) {
|
|
||||||
return {
|
|
||||||
params: l.substring(0, colIndex - 1).split(' '),
|
|
||||||
query: l.substring(colIndex + 1)
|
|
||||||
}
|
|
||||||
} else return { params: l.split(' ') }
|
|
||||||
}
|
|
||||||
|
|
||||||
const servers = []
|
|
||||||
const users = {}
|
|
||||||
const channels = {}
|
|
||||||
|
|
||||||
const globalCommands = {
|
|
||||||
// PING :42X
|
|
||||||
// params: SID
|
|
||||||
PING: l => {
|
|
||||||
const { query } = parseLine(l)
|
|
||||||
m(`PONG :${query}`)
|
|
||||||
emitter.emit('ping')
|
|
||||||
},
|
|
||||||
// PASS hunter2 TS 6 :42X
|
|
||||||
// params: password, 'TS', TS version, SID
|
|
||||||
PASS: l => {
|
|
||||||
const { query } = parseLine(l)
|
|
||||||
// adds a server
|
|
||||||
servers.push(query)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const serverCommands = {
|
|
||||||
// EUID nik 1 1569146316 +i ~nik localhost6.attlocal.net 0::1 42XAAAAAB * * :nik
|
|
||||||
// params: nickname, hopcount, nickTS, umodes, username, visible hostname, IP address, UID, real hostname, account name, gecos
|
|
||||||
EUID: l => {
|
|
||||||
const { params } = parseLine(l)
|
|
||||||
const user = {
|
|
||||||
nick: params[0],
|
|
||||||
nickTS: params[2],
|
|
||||||
modes: params[3],
|
|
||||||
username: params[4],
|
|
||||||
vhost: params[5],
|
|
||||||
ip: params[6],
|
|
||||||
uid: params[7]
|
|
||||||
}
|
|
||||||
users[user.uid] = user
|
|
||||||
},
|
|
||||||
// SJOIN 1569142987 #test +nt :42XAAAAAB
|
|
||||||
// params: channelTS, channel, simple modes, opt. mode parameters..., nicklist
|
|
||||||
SJOIN: l => {
|
|
||||||
const { params, query } = parseLine(l)
|
|
||||||
const channel = {
|
|
||||||
timestamp: params[0],
|
|
||||||
name: params[1],
|
|
||||||
modes: params.slice(2).join(' '),
|
|
||||||
nicklist: query.split(' ').map(uid => {
|
|
||||||
if (/[^0-9a-zA-Z]/.test(uid[0])) return { uid: uid.slice(1), mode: uid[0] }
|
|
||||||
else return { uid: uid, mode: '' }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
channels[channel.name] = channel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const userCommands = {
|
|
||||||
// :42XAAAAAC PRIVMSG #test :asd
|
|
||||||
// params: target, msg
|
|
||||||
PRIVMSG: (l, source) => {
|
|
||||||
const { params, query } = parseLine(l)
|
|
||||||
emitter.emit('message', users[source].nick, params[0], query)
|
|
||||||
},
|
|
||||||
// :42XAAAAAC JOIN 1569149395 #test +
|
|
||||||
JOIN: (l, source) => {
|
|
||||||
const { params } = parseLine(l)
|
|
||||||
channels[params[1]].nicklist.push({
|
|
||||||
uid: source
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// :42XAAAAAC PART #test :WeeChat 2.6
|
|
||||||
PART: (l, source) => {
|
|
||||||
const { params } = parseLine(l)
|
|
||||||
for (let i = 0; i < channels[params[0]].nicklist.length; i++) {
|
|
||||||
if (channels[params[0]].nicklist[i].uid === source) {
|
|
||||||
channels[params[0]].nicklist.splice(i, 1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
QUIT: (_l, source) => {
|
|
||||||
delete users[source]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parser (l) {
|
|
||||||
const split = l.split(' ')
|
|
||||||
const cmd = split[0]
|
|
||||||
const args = split.slice(1).join(' ')
|
|
||||||
if (globalCommands[cmd]) return globalCommands[cmd](args)
|
|
||||||
if (cmd[0] === ':') {
|
|
||||||
const source = cmd.slice(1)
|
|
||||||
const subcmd = split[1]
|
|
||||||
const subargs = split.slice(2).join(' ')
|
|
||||||
if (servers.indexOf(source) > -1 && serverCommands[subcmd]) serverCommands[subcmd](subargs)
|
|
||||||
if (users[source] && userCommands[subcmd]) userCommands[subcmd](subargs, source)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.on('data', data => {
|
|
||||||
data.split('\r\n')
|
|
||||||
.filter(l => l !== '')
|
|
||||||
.forEach(l => {
|
|
||||||
console.log('< ' + l)
|
|
||||||
parser(l)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports.connect = conf => new Promise((resolve, reject) => {
|
|
||||||
emitter.once('ping', resolve)
|
|
||||||
config = conf
|
|
||||||
socket.connect(config.port)
|
|
||||||
process.on('SIGINT', () => {
|
|
||||||
socket.write('QUIT\r\n')
|
|
||||||
process.exit()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
module.exports.events = emitter
|
|
||||||
|
|
||||||
const genTS = () => Math.trunc((new Date()).getTime() / 1000)
|
|
||||||
const genUID = () => {
|
|
||||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
||||||
var uid = ''
|
|
||||||
for (let i = 0; i < 6; i++) uid += chars.charAt(Math.floor(Math.random() * chars.length))
|
|
||||||
if (users[uid]) return genUID()
|
|
||||||
return config.sid + uid
|
|
||||||
}
|
|
||||||
const getUID = nick => {
|
|
||||||
for (const key in users) if (users[key].nick === nick) return key
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.registerUser = nick => {
|
|
||||||
const user = {
|
|
||||||
nick: nick,
|
|
||||||
nickTS: genTS(),
|
|
||||||
modes: '+i',
|
|
||||||
username: '~' + nick,
|
|
||||||
vhost: config.vhost,
|
|
||||||
ip: '0::1',
|
|
||||||
uid: genUID()
|
|
||||||
}
|
|
||||||
users[user.uid] = user
|
|
||||||
m(`EUID ${user.nick} 1 ${user.nickTS} ${user.modes} ~${user.nick} ${user.vhost} 0::1 ${user.uid} * * :${user.nick}`)
|
|
||||||
}
|
|
||||||
module.exports.unregisterUser = nick => {
|
|
||||||
const uid = getUID(nick)
|
|
||||||
m(`:${uid} QUIT :Quit: satyr`)
|
|
||||||
delete users[uid]
|
|
||||||
}
|
|
||||||
module.exports.join = (nick, channelName) => {
|
|
||||||
const uid = getUID(nick)
|
|
||||||
if (!channels[channelName]) {
|
|
||||||
const channel = {
|
|
||||||
timestamp: genTS(),
|
|
||||||
name: channelName,
|
|
||||||
modes: '+nt',
|
|
||||||
nicklist: [{ uid: uid, mode: '' }]
|
|
||||||
}
|
|
||||||
channels[channel.name] = channel
|
|
||||||
}
|
|
||||||
m(`:${uid} JOIN ${channels[channelName].timestamp} ${channelName} +`)
|
|
||||||
}
|
|
||||||
module.exports.part = (nick, channelName) => {
|
|
||||||
const uid = getUID(nick)
|
|
||||||
m(`:${uid} PART ${channelName} :satyr`)
|
|
||||||
for (let i = 0; i < channels[channelName].nicklist.length; i++) {
|
|
||||||
if (channels[channelName].nicklist[i].uid === uid) {
|
|
||||||
channels[channelName].nicklist.splice(i, 1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports.send = (nick, channelName, message) => {
|
|
||||||
const uid = getUID(nick)
|
|
||||||
m(`:${uid} PRIVMSG ${channelName} :${message}`)
|
|
||||||
emitter.emit('message', nick, channelName, message)
|
|
||||||
}
|
|
|
@ -3,13 +3,14 @@ import * as dirty from "dirty";
|
||||||
import { mkdir, fstat, access } from "fs";
|
import { mkdir, fstat, access } from "fs";
|
||||||
import * as strf from "strftime";
|
import * as strf from "strftime";
|
||||||
import * as db from "./database";
|
import * as db from "./database";
|
||||||
|
import {config} from "./config";
|
||||||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
const { exec, execFile } = require('child_process');
|
const { exec, execFile } = require('child_process');
|
||||||
|
|
||||||
const keystore = dirty();
|
const keystore = dirty();
|
||||||
|
|
||||||
function init (mediaconfig: any, satyrconfig: any) {
|
function init () {
|
||||||
const nms = new NodeMediaServer(mediaconfig);
|
const nms = new NodeMediaServer({logType: 0,rtmp: config['rtmp']});
|
||||||
nms.run();
|
nms.run();
|
||||||
|
|
||||||
nms.on('postPublish', (id, StreamPath, args) => {
|
nms.on('postPublish', (id, StreamPath, args) => {
|
||||||
|
@ -23,7 +24,7 @@ function init (mediaconfig: any, satyrconfig: any) {
|
||||||
session.reject();
|
session.reject();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(app !== satyrconfig.privateEndpoint){
|
if(app !== config['media']['privateEndpoint']){
|
||||||
//app isn't at public endpoint if we've reached this point
|
//app isn't at public endpoint if we've reached this point
|
||||||
console.log("[NodeMediaServer] Wrong endpoint, rejecting stream:",id);
|
console.log("[NodeMediaServer] Wrong endpoint, rejecting stream:",id);
|
||||||
session.reject();
|
session.reject();
|
||||||
|
@ -36,21 +37,21 @@ function init (mediaconfig: any, satyrconfig: any) {
|
||||||
if(results[0]){
|
if(results[0]){
|
||||||
//transcode to mpd after making sure directory exists
|
//transcode to mpd after making sure directory exists
|
||||||
keystore[results[0].username] = key;
|
keystore[results[0].username] = key;
|
||||||
mkdir(satyrconfig.directory+'/'+satyrconfig.publicEndpoint+'/'+results[0].username, { recursive : true }, ()=>{;});
|
mkdir(config['http']['directory']+'/'+config['media']['publicEndpoint']+'/'+results[0].username, { recursive : true }, ()=>{;});
|
||||||
while(true){
|
while(true){
|
||||||
if(session.audioCodec !== 0 && session.videoCodec !== 0){
|
if(session.audioCodec !== 0 && session.videoCodec !== 0){
|
||||||
transCommand(mediaconfig, satyrconfig, results[0].username, key).then((r) => {
|
transCommand(results[0].username, key).then((r) => {
|
||||||
execFile(satyrconfig.ffmpeg, r, {maxBuffer: Infinity});
|
execFile(config['media']['ffmpeg'], r, {maxBuffer: Infinity});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
await sleep(300);
|
await sleep(300);
|
||||||
}
|
}
|
||||||
if(results[0].record_flag && satyrconfig.record){
|
if(results[0].record_flag && config['media']['record']){
|
||||||
console.log('[NodeMediaServer] Initiating recording for stream:',id);
|
console.log('[NodeMediaServer] Initiating recording for stream:',id);
|
||||||
mkdir(satyrconfig.directory+'/'+satyrconfig.publicEndpoint+'/'+results[0].username, { recursive : true }, (err) => {
|
mkdir(config['http']['directory']+'/'+config['media']['publicEndpoint']+'/'+results[0].username, { recursive : true }, (err) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
execFile(satyrconfig.ffmpeg, ['-loglevel', 'fatal', '-i', 'rtmp://127.0.0.1:'+mediaconfig.rtmp.port+'/'+satyrconfig.prviateEndpoint+'/'+key, '-vcodec', 'copy', '-acodec', 'copy', satyrconfig.directory+'/'+satyrconfig.publicEndpoint+'/'+results[0].username+'/'+strf('%d%b%Y-%H%M')+'.mp4'], {
|
execFile(config['media']['ffmpeg'], ['-loglevel', 'fatal', '-i', 'rtmp://127.0.0.1:'+config['rtmp']['port']+'/'+config['media']['privateEndpoint']+'/'+key, '-vcodec', 'copy', '-acodec', 'copy', config['http']['directory']+'/'+config['media']['publicEndpoint']+'/'+results[0].username+'/'+strf('%d%b%Y-%H%M')+'.mp4'], {
|
||||||
detached : true,
|
detached : true,
|
||||||
stdio : 'inherit',
|
stdio : 'inherit',
|
||||||
maxBuffer: Infinity
|
maxBuffer: Infinity
|
||||||
|
@ -74,7 +75,7 @@ function init (mediaconfig: any, satyrconfig: any) {
|
||||||
nms.on('donePublish', (id, StreamPath, args) => {
|
nms.on('donePublish', (id, StreamPath, args) => {
|
||||||
let app: string = StreamPath.split("/")[1];
|
let app: string = StreamPath.split("/")[1];
|
||||||
let key: string = StreamPath.split("/")[2];
|
let key: string = StreamPath.split("/")[2];
|
||||||
if(app === satyrconfig.privateEndpoint) {
|
if(app === config['media']['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) => {
|
||||||
if(results[0]) keystore.rm(results[0].username);
|
if(results[0]) keystore.rm(results[0].username);
|
||||||
|
@ -94,24 +95,24 @@ function init (mediaconfig: any, satyrconfig: any) {
|
||||||
}
|
}
|
||||||
//localhost can play from whatever endpoint
|
//localhost can play from whatever endpoint
|
||||||
//other clients must use private endpoint
|
//other clients must use private endpoint
|
||||||
if(app !== satyrconfig.publicEndpoint && !session.isLocal) {
|
if(app !== config['media']['publicEndpoint'] && !session.isLocal) {
|
||||||
console.log("[NodeMediaServer] Non-local Play from private endpoint, rejecting client:",id);
|
console.log("[NodeMediaServer] Non-local Play from private endpoint, rejecting client:",id);
|
||||||
session.reject();
|
session.reject();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//rewrite playpath to private endpoint serverside
|
//rewrite playpath to private endpoint serverside
|
||||||
//(hopefully)
|
//(hopefully)
|
||||||
if(app === satyrconfig.publicEndpoint) {
|
if(app === config['media']['publicEndpoint']) {
|
||||||
if(keystore[key]){
|
if(keystore[key]){
|
||||||
session.playStreamPath = '/'+satyrconfig.privateEndpoint+'/'+keystore[key];
|
session.playStreamPath = '/'+config['media']['privateEndpoint']+'/'+keystore[key];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function transCommand(config: object, satyrconfig: object, user: string, key: string): Promise<string[]>{
|
async function transCommand(user: string, key: string): Promise<string[]>{
|
||||||
let args: string[] = ['-loglevel', 'fatal', '-y', '-i', 'rtmp://127.0.0.1:'+config['rtmp']['port']+'/'+satyrconfig['privateEndpoint']+'/'+key];
|
let args: string[] = ['-loglevel', 'fatal', '-y', '-i', 'rtmp://127.0.0.1:'+config['rtmp']['port']+'/'+config['media']['privateEndpoint']+'/'+key];
|
||||||
if(config['transcode']['adaptive']===true && config['transcode']['variants'] > 1) {
|
if(config['transcode']['adaptive']===true && config['transcode']['variants'] > 1) {
|
||||||
for(let i=0;i<config['transcode']['variants'];i++){
|
for(let i=0;i<config['transcode']['variants'];i++){
|
||||||
args = args.concat(['-map', '0:2']);
|
args = args.concat(['-map', '0:2']);
|
||||||
|
@ -133,9 +134,9 @@ async function transCommand(config: object, satyrconfig: object, user: string, k
|
||||||
args = args.concat(['-c:a', 'copy', '-c:v', 'copy']);
|
args = args.concat(['-c:a', 'copy', '-c:v', 'copy']);
|
||||||
}
|
}
|
||||||
if(config['transcode']['format'] === 'dash')
|
if(config['transcode']['format'] === 'dash')
|
||||||
args = args.concat(['-remove_at_exit', '1', '-seg_duration', '1', '-window_size', '30', '-f', 'dash', satyrconfig['directory']+'/'+satyrconfig['publicEndpoint']+'/'+user+'/index.mpd']);
|
args = args.concat(['-remove_at_exit', '1', '-seg_duration', '1', '-window_size', '30', '-f', 'dash', config['http']['directory']+'/'+config['media']['publicEndpoint']+'/'+user+'/index.mpd']);
|
||||||
else if(config['transcode']['format'] === 'hls')
|
else if(config['transcode']['format'] === 'hls')
|
||||||
args = args.concat(['-remove_at_exit', '1', '-hls_time', '1', '-hls_list_size', '30', '-f', 'hls', satyrconfig['directory']+'/'+satyrconfig['publicEndpoint']+'/'+user+'/index.m3u8']);
|
args = args.concat(['-remove_at_exit', '1', '-hls_time', '1', '-hls_list_size', '30', '-f', 'hls', config['http']['directory']+'/'+config['media']['publicEndpoint']+'/'+user+'/index.m3u8']);
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
export { init };
|
export { init };
|
Reference in New Issue