Merge branch 'invite-codes' into 'develop'
Invite codes See merge request knotteye/satyr!26merge-requests/26/merge
commit
99879fd91e
|
@ -91,7 +91,9 @@ Register a new user.
|
||||||
|
|
||||||
**Authentication**: no
|
**Authentication**: no
|
||||||
|
|
||||||
**Parameters**: Username, password, confirm
|
**Parameters**: Username, password, confirm, invite(optional)
|
||||||
|
|
||||||
|
Invite is an optional invite code to bypass disabled registration.
|
||||||
|
|
||||||
**Response**: If successful, returns a json object with the users stream key. Otherwise returns `{error: "error reason"}`
|
**Response**: If successful, returns a json object with the users stream key. Otherwise returns `{error: "error reason"}`
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
{
|
{
|
||||||
"name": "satyr",
|
"name": "satyr",
|
||||||
"version": "0.9.3",
|
"version": "0.9.4",
|
||||||
"description": "A livestreaming server.",
|
"description": "A livestreaming server.",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"author": "knotteye",
|
"author": "knotteye",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "ts-node src/index.ts",
|
"start": "ts-node src/index.ts",
|
||||||
"user": "ts-node src/cli.ts",
|
"cli": "ts-node src/cli.ts",
|
||||||
"setup": "sh install/setup.sh",
|
"setup": "sh install/setup.sh",
|
||||||
"migrate": "ts-node src/migrate.ts"
|
"migrate": "ts-node src/migrate.ts",
|
||||||
|
"invite": "ts-node src/cli.ts --invite"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
24
src/api.ts
24
src/api.ts
|
@ -1,9 +1,10 @@
|
||||||
import * as db from "./database";
|
import * as db from "./database";
|
||||||
|
import * as base64id from "base64id";
|
||||||
import { config } from "./config";
|
import { config } from "./config";
|
||||||
import {unlink} from "fs";
|
import {unlink} from "fs";
|
||||||
|
|
||||||
async function register(name: string, password: string, confirm: string): Promise<object> {
|
async function register(name: string, password: string, confirm: string, invite?: boolean): Promise<object> {
|
||||||
if(!config['satyr']['registration']) return {"error":"registration disabled"};
|
if(!config['satyr']['registration'] && !invite) 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['satyr']['restrictedNames'].length;i++){
|
for(let i=0;i<config['satyr']['restrictedNames'].length;i++){
|
||||||
|
@ -97,4 +98,21 @@ async function getConfig(username: string, all?: boolean): Promise<object>{
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { register, update, changepwd, changesk, login, updateChat, deleteVODs, getConfig };
|
async function genInvite(): Promise<string>{
|
||||||
|
var invitecode: string = base64id.generateId();
|
||||||
|
await db.query('INSERT INTO invites (code) VALUES (\"'+invitecode+'\")');
|
||||||
|
return invitecode;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validInvite(code: string): Promise<boolean>{
|
||||||
|
if(typeof(code) !== "string" || code === "") return false;
|
||||||
|
var result = await db.query('SELECT code FROM invites WHERE code='+db.raw.escape(code));
|
||||||
|
if(!result[0] || result[0]['code'] !== code) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function useInvite(code: string): Promise<void>{
|
||||||
|
if(validInvite(code)) await db.query('DELETE FROM invites WHERE code='+db.raw.escape(code));
|
||||||
|
}
|
||||||
|
|
||||||
|
export { register, update, changepwd, changesk, login, updateChat, deleteVODs, getConfig, genInvite, useInvite, validInvite };
|
13
src/cli.ts
13
src/cli.ts
|
@ -1,4 +1,5 @@
|
||||||
import * as db from "./database"
|
import * as db from "./database";
|
||||||
|
import * as api from "./api";
|
||||||
import * as flags from "flags";
|
import * as flags from "flags";
|
||||||
|
|
||||||
db.init();
|
db.init();
|
||||||
|
@ -6,6 +7,7 @@ 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');
|
||||||
flags.defineString('password', '', 'password to hash');
|
flags.defineString('password', '', 'password to hash');
|
||||||
|
flags.defineBoolean('invite', false, 'generate invite code');
|
||||||
|
|
||||||
flags.parse();
|
flags.parse();
|
||||||
|
|
||||||
|
@ -24,3 +26,12 @@ if(flags.get('rmuser') !== ''){
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(flags.get('invite')){
|
||||||
|
var config = require("./config").config;
|
||||||
|
api.genInvite().then((r: string) => {
|
||||||
|
console.log('invite code: '+r);
|
||||||
|
console.log('Direct the user to https://'+config['satyr']['domain']+'/invite/'+r);
|
||||||
|
process.exit();
|
||||||
|
});
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ const config: Object = {
|
||||||
domain: '',
|
domain: '',
|
||||||
registration: false,
|
registration: false,
|
||||||
email: null,
|
email: null,
|
||||||
restrictedNames: [ 'live', 'user', 'users', 'register', 'login' ],
|
restrictedNames: [ 'live', 'user', 'users', 'register', 'login', 'invite' ],
|
||||||
rootredirect: '/users/live',
|
rootredirect: '/users/live',
|
||||||
version: process.env.npm_package_version,
|
version: process.env.npm_package_version,
|
||||||
}, localconfig['satyr']),
|
}, localconfig['satyr']),
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import * as db from "../database";
|
||||||
|
|
||||||
|
async function run () {
|
||||||
|
await db.query('CREATE TABLE IF NOT EXISTS invites(code VARCHAR(150))');
|
||||||
|
await db.query('INSERT INTO db_meta (version) VALUES (2)');
|
||||||
|
}
|
||||||
|
|
||||||
|
export { run }
|
23
src/http.ts
23
src/http.ts
|
@ -224,6 +224,23 @@ async function initAPI() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
app.post('/api/register', (req, res) => {
|
app.post('/api/register', (req, res) => {
|
||||||
|
if("invite" in req.body){
|
||||||
|
api.validInvite(req.body.invite).then((v) => {
|
||||||
|
if(v){
|
||||||
|
api.register(req.body.username, req.body.password, req.body.confirm, true).then((result) => {
|
||||||
|
if(result[0]) return genToken(req.body.username).then((t) => {
|
||||||
|
res.cookie('Authorization', t, {maxAge: 604800000, httpOnly: true, sameSite: 'Lax'});
|
||||||
|
res.json(result);
|
||||||
|
api.useInvite(req.body.invite);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
res.json(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else res.json({error: "invalid invite code"});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
api.register(req.body.username, req.body.password, req.body.confirm).then( (result) => {
|
api.register(req.body.username, req.body.password, req.body.confirm).then( (result) => {
|
||||||
if(result[0]) return genToken(req.body.username).then((t) => {
|
if(result[0]) return genToken(req.body.username).then((t) => {
|
||||||
res.cookie('Authorization', t, {maxAge: 604800000, httpOnly: true, sameSite: 'Lax'});
|
res.cookie('Authorization', t, {maxAge: 604800000, httpOnly: true, sameSite: 'Lax'});
|
||||||
|
@ -486,6 +503,12 @@ async function initSite(openReg) {
|
||||||
}
|
}
|
||||||
else res.render('login.njk',njkconf);
|
else res.render('login.njk',njkconf);
|
||||||
});
|
});
|
||||||
|
app.get('/invite/:code', (req, res) => {
|
||||||
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
|
res.redirect('/profile');
|
||||||
|
}
|
||||||
|
else res.render('invite.njk',Object.assign({icode: req.params.code}, njkconf));
|
||||||
|
});
|
||||||
app.get('/register', (req, res) => {
|
app.get('/register', (req, res) => {
|
||||||
if(tryDecode(req.cookies.Authorization) || !openReg) {
|
if(tryDecode(req.cookies.Authorization) || !openReg) {
|
||||||
res.redirect(njkconf.rootredirect);
|
res.redirect(njkconf.rootredirect);
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
{% extends "base.njk" %}
|
||||||
|
{% block content %}
|
||||||
|
<h3>You've been invited to {{ sitename }}</h3><span style="font-size: small;">Already registered? Log in <a href="/login">here</a>.</br></br></span>
|
||||||
|
<!--<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">
|
||||||
|
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>
|
||||||
|
Confirm: </br><input type="password" name="confirm" style="min-width: 300px"/></br></br>
|
||||||
|
<input type="hidden" name="invite" style="min-width: 300px" value="{{icode}}"/>
|
||||||
|
<input type="submit" value="Submit">
|
||||||
|
</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>
|
||||||
|
<!--</div>
|
||||||
|
</div>-->
|
||||||
|
{% endblock %}
|
Reference in New Issue