added basic rest api

This commit is contained in:
Daniel Sommer 2022-04-22 15:58:04 +02:00
parent e3a30f9396
commit 0fa7ca78a6
7 changed files with 122 additions and 30 deletions

59
classes/Api.js Normal file
View file

@ -0,0 +1,59 @@
const AudioServer = require("./AudioServer");
class Api {
constructor() {
this.get = new Map();
this.post = new Map();
this.#setup();
}
async handleRequest(request) {
if (request === undefined) {
return this.#createRequestResult(500);
}
const fn = this.#getFunction(request.url, request.method.toLowerCase());
if (fn === undefined) {
return this.#createRequestResult(501);
}
await fn();
return this.#createRequestResult();
}
#createRequestResult(code, message) {
if (code === undefined) {
code = 200;
}
const result = {
code: code
};
if (message !== undefined) {
result.message = message;
}
return result;
}
#setup() {
this.#registerEndpoint(constants.API_PLAY, constants.REQUEST_METHOD_POST, () => {
new AudioServer('/mnt/kingston/public/LEFTOVER.flac');
});
}
#getFunction(url, method) {
if (url === undefined || method === undefined || this[method] === undefined) {
return;
}
return this[method].get(url);
}
#registerEndpoint(url, method, fn) {
if (url === undefined || method === undefined || fn === undefined || this[method] === undefined) {
return false;
}
this[method].set(url, fn);
return true;
}
}
module.exports = Api;

43
classes/ApiServer.js Normal file
View file

@ -0,0 +1,43 @@
const Api = require('./Api.js');
const http = require('http');
class ApiServer {
constructor() {
this.listen = config.api?.listen || "0.0.0.0";
this.port = config.api?.port || 9000;
this.api = new Api();
this.server = http.createServer();
this.requestId = 0;
}
start() {
return new Promise((resolve, reject) => {
this.server.listen(this.port, this.listen).on('listening', () => {
this.port = this.server.address().port;
logger.info('api server listening on ' + this.listen + ':' + this.port + '...');
this.#handleRequests();
resolve();
});
this.server.on('error', (err) => {
reject('an unexpected error occured: ' + err);
});
});
}
#handleRequests() {
this.server.on('request', async (request, response) => {
request.timestamp = Date.now();
request.id = this.requestId;
this.requestId++;
logger.debug('handling api request #' + request.id + ' \'' + request.url + '\'...');
const result = await this.api.handleRequest(request);
response.writeHead(result.code);
response.end(result.message);
logger.debug('handling api request #' + request.id + '\'' + request.url + '\' took ' + (Date.now() - request.timestamp) + 'ms');
});
}
}
module.exports = ApiServer;

View file

@ -48,30 +48,6 @@ class Logger {
this.timestamp = value || 'DD.MM.YYYY HH:mm:ss:SS'; this.timestamp = value || 'DD.MM.YYYY HH:mm:ss:SS';
} }
// log a http request - response object
http(object) {
if (object === undefined) {
return;
}
let message = '[' + object.request.method + ':' + object.code + '] url: \'' + object.request.url + '\'';
let counter = 1;
for (let param in object.request.body) {
message += ', parameter ' + counter + ': \'' + param + '=' + object.request.body[param] + '\'';
counter++;
}
if (object.request.timestamp) {
message += ' > ' + (new Date().getTime() - object.request.timestamp) + 'ms';
}
if (object.data) {
message += ' > data: ' + object.data;
}
if (object.code != 200) {
error(message.trim());
return;
}
this.debug(message.trim());
}
// prefix log with 'info' // prefix log with 'info'
info(message) { info(message) {
if (this.loglevel > LOGLEVEL_INFO) { if (this.loglevel > LOGLEVEL_INFO) {

View file

@ -1,8 +1,6 @@
const net = require('net');
const Client = require('./Client.js'); const Client = require('./Client.js');
const AudioServer = require('./AudioServer.js'); const AudioServer = require('./AudioServer.js');
const net = require('net');
class Server { class Server {
@ -18,6 +16,7 @@ class Server {
this.server.listen(this.port, this.listen).on('listening', () => { this.server.listen(this.port, this.listen).on('listening', () => {
this.port = this.server.address().port; this.port = this.server.address().port;
logger.info('communication server listening on ' + this.listen + ':' + this.port + '...'); logger.info('communication server listening on ' + this.listen + ':' + this.port + '...');
resolve();
}); });
this.server.on('connection', (socket) => { this.server.on('connection', (socket) => {
this.#addClient(socket); this.#addClient(socket);
@ -44,7 +43,6 @@ class Server {
#addClient(socket) { #addClient(socket) {
this.clients.push(new Client(socket)); this.clients.push(new Client(socket));
new AudioServer('/mnt/kingston/public/DOPESMOKER.flac');
} }
removeClient(client) { removeClient(client) {

View file

@ -5,6 +5,11 @@
"port": 3000, "port": 3000,
"heartbeat": 10000 "heartbeat": 10000
}, },
"api": {
"enabled": true,
"listen": "0.0.0.0",
"port": 9000
},
"log": { "log": {
"level": "debug", "level": "debug",
"timestamp": "DD.MM.YYYY HH:mm:ss:SS" "timestamp": "DD.MM.YYYY HH:mm:ss:SS"
@ -22,7 +27,7 @@
"password": "kannon" "password": "kannon"
}, },
"audio": { "audio": {
"threshold": 30, "threshold": 10,
"bufferlimit": 64 "bufferlimit": 64
} }
} }

View file

@ -8,6 +8,7 @@ const Database = require('./classes/Database.js');
const Queue = require('./classes/Queue.js'); const Queue = require('./classes/Queue.js');
const Watcher = require('./classes/Watcher.js'); const Watcher = require('./classes/Watcher.js');
const EventParser = require('./classes/EventParser'); const EventParser = require('./classes/EventParser');
const ApiServer = require('./classes/ApiServer.js');
const INTERRUPTS = ['beforeExit', 'SIGINT', 'SIGTERM']; const INTERRUPTS = ['beforeExit', 'SIGINT', 'SIGTERM'];
@ -36,6 +37,11 @@ async function main() {
global.server = new Server(); global.server = new Server();
await global.server.start(); await global.server.start();
} }
// api server
if (util.isEnabled(global.config.api)) {
global.apiserver = new ApiServer();
global.apiserver.start();
}
// queue and watcher // queue and watcher
if (util.isEnabled(global.config.library)) { if (util.isEnabled(global.config.library)) {
global.queue = new Queue(); global.queue = new Queue();

View file

@ -10,5 +10,10 @@ module.exports = {
CLIENT_STATE_PLAYING: 'playing', CLIENT_STATE_PLAYING: 'playing',
CLIENT_STATE_PAUSED: 'paused', CLIENT_STATE_PAUSED: 'paused',
CLIENT_STATE_STOPPED: 'stopped', CLIENT_STATE_STOPPED: 'stopped',
CLIENT_STATE_ERROR: 'error' CLIENT_STATE_ERROR: 'error',
REQUEST_METHOD_GET: 'get',
REQUEST_METHOD_POST: 'post',
API_PLAY: '/play'
} }