From 654b65640f32f8a2c21f690a10df972b641de26c Mon Sep 17 00:00:00 2001 From: knotteye Date: Mon, 3 Aug 2020 05:01:20 -0500 Subject: [PATCH] Change API to set content-type headers. Rework some responses to make all responses in JSON. Increment version because of major API changes. --- docs/REST.md | 8 ++-- package.json | 2 +- src/api.ts | 12 +++--- src/http.ts | 102 +++++++++++++++++++++++++-------------------------- 4 files changed, 61 insertions(+), 63 deletions(-) diff --git a/docs/REST.md b/docs/REST.md index 67bbf8b..3f52bb7 100644 --- a/docs/REST.md +++ b/docs/REST.md @@ -68,9 +68,11 @@ sort is the optional way to sort results. current options are "alphabet" and "al page is the page number (i.e. skip the first num * page results) -**Response**: Returns an array of objects containing the username and title of each stream. Returns an empty array if no one is streaming. Returns `{"error":"error code"}` in the event of an error. Will attempt to correct malformed requests to default values. +**Response**: Returns an array of JSON objects containing the username and title of each stream. Returns an empty array if no one is streaming. Returns `{"error":"error code"}` in the event of an error. Will attempt to correct malformed requests to default values. -**Example**: `[{username:"foo", title:"bar"}]` +The array will be wrapped in a JSON object under the key 'users'. + +**Example**: `{users: [{username:"foo", title:"bar"}] }` ## /api/users/all @@ -194,7 +196,7 @@ Get a list of the named users VODs **Parameters**: user -**Response**: Returns an array of VODs with the format `[{"name":"yote.mp4"},{"name":"yeet.mp4"}]` +**Response**: Returns an array of VODs inside a JSON object with the format `{"vods": [{"name":"yote.mp4"},{"name":"yeet.mp4"}] }` **Notes**: VODs are always available at http://domain.com/publicEndpoint/username/filename.mp4 diff --git a/package.json b/package.json index 8e509e1..2e1b572 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "satyr", - "version": "0.8.0", + "version": "0.9.0", "description": "A livestreaming server.", "license": "AGPL-3.0", "author": "knotteye", diff --git a/src/api.ts b/src/api.ts index 3883abe..bcc79ae 100644 --- a/src/api.ts +++ b/src/api.ts @@ -14,7 +14,7 @@ async function register(name: string, password: string, confirm: string): Promis let k = await db.query('select stream_key from users where username='+db.raw.escape(name)); return k; } - return {"error":""}; + return {error:""}; } async function update(fields: object): Promise{ @@ -32,12 +32,12 @@ async function update(fields: object): Promise{ 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 updateChat(fields: object): Promise{ await db.query('UPDATE chat_integration SET xmpp='+db.raw.escape(fields['xmpp'])+', discord='+db.raw.escape(fields['discord'])+', irc='+db.raw.escape(fields['irc'])+', twitch='+db.raw.escape(fields['twitch'])+' WHERE username='+db.raw.escape(fields['name'])); - return {"success":""}; + return {success:""}; } async function changepwd(name: string, password: string, newpwd: string): Promise{ @@ -46,13 +46,13 @@ async function changepwd(name: string, password: string, newpwd: string): Promis if(!auth) return {"error":"Username or Password Incorrect"}; let newhash: string = await db.hash(newpwd); await db.query('UPDATE users set password_hash='+db.raw.escape(newhash)+'where username='+db.raw.escape(name)+' limit 1'); - return {"success":""}; + return {success:""}; } async function changesk(name: string): Promise{ let key: string = await db.genKey(); await db.query('UPDATE users set stream_key='+db.raw.escape(key)+'where username='+db.raw.escape(name)+' limit 1'); - return {"success":key}; + return {success: key}; } async function login(name: string, password: string){ @@ -66,7 +66,7 @@ async function deleteVODs(vodlist: Array, username: string): Promise{}); } - return {"success":""}; + return {success: ""}; } async function getConfig(username: string, all?: boolean): Promise{ diff --git a/src/http.ts b/src/http.ts index 6d9bdba..ef4e127 100644 --- a/src/http.ts +++ b/src/http.ts @@ -141,31 +141,27 @@ async function parseCookie(c){ async function initAPI() { app.get('/api/instance/info', (req, res) => { - res.send( - JSON.stringify({ - name: config['satyr']['name'], - domain: config['satyr']['domain'], - registration: config['satyr']['registration'], - version: config['satyr']['version'], - email: config['satyr']['email'] - }) - ); + res.json({ + name: config['satyr']['name'], + domain: config['satyr']['domain'], + registration: config['satyr']['registration'], + version: config['satyr']['version'], + email: config['satyr']['email'] + }); }); app.get('/api/instance/config', (req, res) => { - res.send( - JSON.stringify({ - rtmp: { - port: config['rtmp']['port'], - ping_timeout: config['rtmp']['ping_timeout'] - }, - media: { - vods: config['config']['media']['record'], - publicEndpoint: config['media']['publicEndpoint'], - privateEndpoint: config['media']['privateEndpoint'], - adaptive: config['transcode']['adaptive'] - } - }) - ); + res.json({ + rtmp: { + port: config['rtmp']['port'], + ping_timeout: config['rtmp']['ping_timeout'] + }, + media: { + vods: config['config']['media']['record'], + publicEndpoint: config['media']['publicEndpoint'], + privateEndpoint: config['media']['privateEndpoint'], + adaptive: config['transcode']['adaptive'] + } + }); }); app.post('/api/users/live', (req, res) => { let qs = 'SELECT username,title FROM user_meta WHERE live=1'; @@ -193,8 +189,8 @@ async function initAPI() { } db.query(qs+';').then((result) => { - if(result) res.send(result); - else res.send('{"error":""}'); + if(result) res.json({users: result}); + else res.json({error:""}); }); }); app.post('/api/users/all', (req, res) => { @@ -223,18 +219,18 @@ async function initAPI() { } db.query(qs+';').then((result) => { - if(result) res.send(result); - else res.send('{"error":""}'); + if(result) res.json({users: result}); + else res.json({error:""}); }); }); 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, {maxAge: 604800000, httpOnly: true, sameSite: 'Lax'}); - res.send(result); + res.json(result); return; }); - res.send(result); + res.json(result); }); }); app.post('/api/user/update', (req, res) => { @@ -247,12 +243,12 @@ async function initAPI() { bio: "bio" in req.body ? req.body.bio : false, rec: "record" in req.body ? req.body.record : "NA" }).then((r) => { - res.send(r); + res.json(r); return; }); } else { - res.send('{"error":"invalid token"}'); + res.json({error:"invalid token"}); return; } }); @@ -269,12 +265,12 @@ async function initAPI() { twitch: "twitch" in req.body ? req.body.twitch : false, irc: "irc" in req.body ? req.body.irc : false, }).then((r) => { - res.send(r); + res.json(r); return; }); } else { - res.send('{"error":"invalid token"}'); + res.json({error:"invalid token"}); return; } }); @@ -284,19 +280,19 @@ async function initAPI() { }); app.post('/api/user/vods/delete', (req, res) => { if(req.body.vlist === undefined || req.body.vlist === null || req.body.vlist === []){ - res.send('{"error":"no vods specified"}'); + res.json({error:"no vods specified"}); return; } validToken(req.cookies.Authorization).then((t) => { if(t) { //token is valid, process deletion request return api.deleteVODs(req.body.vlist, t['username']).then((r)=> { - res.send(r) + res.json(r) return; }); } else { - res.send('{"error":"invalid token"}'); + res.json({error:"invalid token"}); return; } }); @@ -305,12 +301,12 @@ async function initAPI() { validToken(req.cookies.Authorization).then((t) => { if(t) { return api.changepwd(t['username'], req.body.password, req.body.newpassword).then((r) => { - res.send(r); + res.json(r); return; }); } else { - res.send('{"error":"invalid token"}'); + res.json({error:"invalid token"}); return; } }); @@ -319,12 +315,12 @@ async function initAPI() { validToken(req.cookies.Authorization).then((t) => { if(t) { db.query('SELECT stream_key FROM users WHERE username='+db.raw.escape(t['username'])).then(o => { - if(o[0]) res.send(o[0]); - else res.send('{"error":""}'); + if(o[0]) res.json(o[0]); + else res.json({error:""}); }); } else { - res.send('{"error":"invalid token"}'); + res.json({error:"invalid token"}); } }); }); @@ -332,11 +328,11 @@ async function initAPI() { validToken(req.cookies.Authorization).then((t) => { if(t) { api.changesk(t['username']).then((r) => { - res.send(r); + res.json(r); }); } else { - res.send('{"error":"invalid token"}'); + res.json({error:"invalid token"}); } }); }); @@ -346,17 +342,17 @@ async function initAPI() { if(t['exp'] - 86400 < Math.floor(Date.now() / 1000)){ return genToken(t['username']).then((t) => { res.cookie('Authorization', t, {maxAge: 604800000, httpOnly: true, sameSite: 'Lax'}); - res.send('{"success":""}'); + res.json({success:""}); return; }); } else { - res.send('{"success":"already verified"}'); + res.json({success:"already verified"}); return; } } else { - res.send('{"error":"invalid token"}'); + res.json({error:"invalid token"}); return; } }); @@ -365,11 +361,11 @@ async function initAPI() { if(!result){ genToken(req.body.username).then((t) => { res.cookie('Authorization', t, {maxAge: 604800000, httpOnly: true, sameSite: 'Lax'}); - res.send('{"success":""}'); + res.json({success:""}); }) } else { - res.send(result); + res.json(result); } }); } @@ -377,28 +373,28 @@ async function initAPI() { app.get('/api/:user/vods', (req, res) => { readdir('./site/live/'+req.params.user, {withFileTypes: true} , (err, files) => { if(err) { - res.send([]); + res.json({vods: []}); return; } var list = files.filter(fn => fn.name.endsWith('.mp4')); - res.send(list); + res.json({vods: list}); }); }); app.get('/api/:user/config', (req, res) => { if(req.cookies.Authorization) validToken(req.cookies.Authorization).then(r => { if(r && r['username'] === req.params.user) { api.getConfig(req.params.user, true).then(re => { - res.send(JSON.stringify(re)); + res.json(re); }); return; } else api.getConfig(req.params.user).then(re => { - res.send(JSON.stringify(re)); + res.json(re); }); return; }); else api.getConfig(req.params.user).then(r => { - res.send(JSON.stringify(r)); + res.json(r); }); }); }