diff --git a/src/config/configuration.js b/src/config/configuration.js new file mode 100644 index 0000000..c8aa9a2 --- /dev/null +++ b/src/config/configuration.js @@ -0,0 +1,38 @@ +const path = require('path'); +const Field = require('./field.js'); + +let instance = null; + +class Config { + constructor() { + this.location = Config.determineLocation(); + this.fields = require(`${this.location}`); + } + + static getInstance() { + if (instance == null) { + instance = new Config(); + } + return instance; + } + + static determineLocation() { + return path.join(__dirname, '..', '..', process.env.SEASONED_CONFIG); + } + + get(section, option) { + if (this.fields[section] === undefined || this.fields[section][option] === undefined) { + throw new Error(`Filed "${section} => ${option}" does not exist.`); + } + + const field = new Field(this.fields[section][option]) + + if (field.value === undefined) { + throw new Error(`${section} => ${option} is empty.`); + } + + return field.value; + } +} + +module.exports = Config; \ No newline at end of file diff --git a/src/config/environmentVariables.js b/src/config/environmentVariables.js new file mode 100644 index 0000000..d879fe7 --- /dev/null +++ b/src/config/environmentVariables.js @@ -0,0 +1,16 @@ +class EnvironmentVariables { + + constructor(variables) { + this.variables = variables || process.env; + } + + get(variable) { + return this.variables[variable]; + } + + has(variable) { + return this.get(variable) !== undefined; + } +} + +module.exports = EnvironmentVariables; diff --git a/src/config/field.js b/src/config/field.js new file mode 100644 index 0000000..2dbfc58 --- /dev/null +++ b/src/config/field.js @@ -0,0 +1,50 @@ +const Filters = require('./filters.js'); +const EnvironmentVariables = require('./environmentVariables.js'); + +class Field { + + constructor(rawValue, environmentVariables) { + this.rawValue, rawValue; + this.filters = new Filters(rawValue); + this.valueWithoutFilters = this.filters.removeFiltersFromValue(); + this.environmentVariables = new EnvironmentVariables(environmentVariables); + } + + get value() { + if (this.filters.isEmpty()) { + return this.valueWithoutFilters; + } + + if (this.filters.has('base64') && !this.filters.has('env')) { + return Field.base64Decode(this.valueWithoutFilters); + } + + if (this.environmentVariables.has(this.valueWithoutFilters) && + this.environmentVariables.get(this.valueWithoutFilters) === '') { + return undefined; + } + + if (!this.filters.has('base64') && this.filters.has('env')) { + if (this.environmentVariables.has(this.valueWithoutFilters)) { + return this.environmentVariables.get(this.valueWithoutFilters); + } + return undefined; + } + + if (this.filters.has('env') && this.filters.has('base64')) { + if (this.environmentVariables.has(this.valueWithoutFilters)) { + const encodedEnvironmentVariable = this.environmentVariables.get(this.valueWithoutFilters); + return Field.base64Decode(encodedEnvironmentVariable); + } + return undefined; + } + + return this.valueWithoutFilters; + } + + static base64Decode(string) { + return new Buffer(string, 'base64').toString('utf-8'); + } +} + +module.exports = Field; \ No newline at end of file diff --git a/src/config/filters.js b/src/config/filters.js new file mode 100644 index 0000000..ebd813d --- /dev/null +++ b/src/config/filters.js @@ -0,0 +1,35 @@ +class Filters { + + constructor(value) { + this.value = value; + this.delimiter = '|'; + } + + get filters() { + return this.value.split(this.delimiter).slice(0, -1); + } + + isEmpty() { + return !this.hasValidType() || this.filters.length === 0; + } + + has(filter) { + return this.filters.includes(filter); + } + + hasValidType() { + return (typeof this.value === 'string'); + } + + removeFiltersFromValue() { + if (this.hasValidType() === false) { + return this.value; + } + + let filtersCombined = this.filters.join(this.delimiter); + filtersCombined += this.filters.length >= 1 ? this.delimiter : ''; + return this.value.replace(filtersCombined, ''); + } +} + +module.exports = Filters; diff --git a/src/database/database.js b/src/database/database.js new file mode 100644 index 0000000..3d5a9f2 --- /dev/null +++ b/src/database/database.js @@ -0,0 +1,15 @@ +const configuration = require('src/config/configuration').getInstance(); +const SqliteDatabase = require('src/database/SqliteDatabase'); +const database = new SqliteDatabase(configuration.get('database', 'host')); + +/** + * This module establishes a connection to the database + * specified in the confgiuration file. It tries to setup + * the required tables after successfully connecting. + * If the tables already exists, it simply proceeds. + */ +Promise.resolve() +.then(() => database.connect()) +// .then(() => database.setUp()); + +module.exports = database; diff --git a/src/database/sqliteDatabase.js b/src/database/sqliteDatabase.js new file mode 100644 index 0000000..ff03f1b --- /dev/null +++ b/src/database/sqliteDatabase.js @@ -0,0 +1,32 @@ +const fs = require('fs'); +const path = require('path'); +const sqlite = require('sqlite'); + +class SqliteDatabase { + + constructor(host) { + this.host = host; + this.connection = sqlite; + + // this.schemaDirectory = path.join(__dirname, 'schemas'); + } + + connect() { + return Promise.resolve() + .then(() => sqlite.open(this.host)) + } + + all(sql, parameters) { + return this.connection.all(sql, parameters); + } + + get(sql, parameters) { + return this.connection.get(sql, parameters); + } + + run(sql, parameters) { + return this.connection.run(sql, parameters); + } +} + +module.exports = SqliteDatabase; \ No newline at end of file diff --git a/src/seasoned/stray.js b/src/seasoned/stray.js new file mode 100644 index 0000000..ce2218f --- /dev/null +++ b/src/seasoned/stray.js @@ -0,0 +1,7 @@ +class Stray { + constructor(id) { + this.id = id; + } +} + +module.exports = Stray; \ No newline at end of file diff --git a/src/seasoned/strayRepository.js b/src/seasoned/strayRepository.js new file mode 100644 index 0000000..5048ab8 --- /dev/null +++ b/src/seasoned/strayRepository.js @@ -0,0 +1,39 @@ +const assert = require('assert'); +const Stray = require('src/seasoned/stray'); +const establishedDatabase = require('src/database/database'); + +class StrayRepository { + + constructor(database) { + this.database = database || establishedDatabase; + this.queries = { + 'read': 'SELECT * FROM stray_eps WHERE id = ?', + 'readAll': 'SELECT id, name, season, episode FROM stray_eps', + 'verify': 'UPDATE stray_eps SET verified = 1 WHERE id = ?', + }; + } + + read(strayId) { + return this.database.get(this.queries.read, strayId).then((row) => { + assert.notEqual(row, undefined, `Could not find list with id ${strayId}.`); + return row; + }) + } + + readAll() { + return this.database.all(this.queries.readAll).then(rows => + rows.map((row) => { + const stray = new Stray(row.id); + stray.name = row.name; + stray.season = row.season; + stray.episode = row.episode; + return stray; + })) + } + + verifyStray(strayId) { + return this.database.run(this.queries.verify, strayId); + } +} + +module.exports = StrayRepository; \ No newline at end of file diff --git a/src/strayEpisode/strayEpisodes.js b/src/strayEpisode/strayEpisodes.js new file mode 100644 index 0000000..68894a7 --- /dev/null +++ b/src/strayEpisode/strayEpisodes.js @@ -0,0 +1,28 @@ +/* +* @Author: KevinMidboe +* @Date: 2017-04-12 16:57:55 +* @Last Modified by: KevinMidboe +* @Last Modified time: 2017-04-12 17:05:28 +*/ + +const assert = require('assert'); +const establishedDatabase = require('src/database/database'); + +class StayEpisodes { + + constructor(database) { + this.databse = database || establishedDatabase; + this.queries = { + 'read': 'SELECT name, season, episode FROM stray_eps WHERE id = ?', + 'readEpisode': 'SELECT * FROM stray_eps WHERE id = ?', + 'updateEpisode': 'UPDATE stray_eps SET name = ?, season = ?, episode = ?, video_files = ?, \ + subtitles = ?, trash = ?, verified = ? WHERE id = ?', + }; + } + + read(episodeId) { + return this.database.get(this.queries.read, episodeId).then((row) => { + assert.notEqual(row, undefined) + }) + } +} \ No newline at end of file diff --git a/src/webserver/app.js b/src/webserver/app.js new file mode 100644 index 0000000..5715b07 --- /dev/null +++ b/src/webserver/app.js @@ -0,0 +1,44 @@ + +var express = require('express'); // call express +var app = express(); // define our app using express +var bodyParser = require('body-parser'); +var sqlite3 = require('sqlite3').verbose(); + + +// configure app to use bodyParser() +// this will let us get the data from a POST +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); + +var port = 31459; // set our port + +// ROUTES FOR OUR API +// ============================================================================= +var router = express.Router(); // get an instance of the express Router + +router.use(function(req, res, next) { + // do logging + console.log('Something is happening.'); + res.setHeader('Access-Control-Allow-Origin', 'https://kevinmidboe.com'); + next(); // make sure we go to the next routes and don't stop here +}); + +// test route to make sure everything is working (accessed at GET http://localhost:8080/api) +router.get('/', function(req, res) { + res.json({ message: 'hooray! welcome to our api!' }); +}); + + +router.get('/v1/seasoned/all', require('./controllers/seasoned/readStrays.js')); +router.get('/v1/seasoned/:strayId', require('./controllers/seasoned/strayById.js')); +router.post('/v1/seasoned/verify/:strayId', require('./controllers/seasoned/verifyStray.js')); + +// router.get('/v1/plex/search', require('./controllers/plex/searchMedia.js')); +// router.post('/v1/plex/request/:mediaId', require('./controllers/plex/request.js')); + + +// REGISTER OUR ROUTES ------------------------------- +// all of our routes will be prefixed with /api +app.use('/api', router); + +module.exports = app; diff --git a/src/webserver/controllers/seasoned/readStrays.js b/src/webserver/controllers/seasoned/readStrays.js new file mode 100644 index 0000000..b347153 --- /dev/null +++ b/src/webserver/controllers/seasoned/readStrays.js @@ -0,0 +1,15 @@ +const StrayRepository = require('src/seasoned/strayRepository'); +const strayRepository = new StrayRepository(); + + +function readStraysController(req, res) { + strayRepository.readAll() + .then((strays) => { + res.send(strays); + }) + .catch((error) => { + res.status(500).send({success: false, error: error.message }); + }); +} + +module.exports = readStraysController; \ No newline at end of file diff --git a/src/webserver/controllers/seasoned/strayById.js b/src/webserver/controllers/seasoned/strayById.js new file mode 100644 index 0000000..aa633da --- /dev/null +++ b/src/webserver/controllers/seasoned/strayById.js @@ -0,0 +1,17 @@ +const configuration = require('src/config/configuration').getInstance(); +const StrayRepository = require('src/seasoned/StrayRepository'); +const strayRepository = new StrayRepository(); + +function strayByIdController(req, res) { + const id = req.params.strayId; + + strayRepository.read(id) + .then((stray) => { + res.send(stray); + }) + .catch((error) => { + res.status(500).send({ success: false, error: error.message }); + }); +} + +module.exports = strayByIdController; \ No newline at end of file diff --git a/src/webserver/controllers/seasoned/verifyStray.js b/src/webserver/controllers/seasoned/verifyStray.js new file mode 100644 index 0000000..0bb4fc4 --- /dev/null +++ b/src/webserver/controllers/seasoned/verifyStray.js @@ -0,0 +1,17 @@ +const configuration = require('src/config/configuration').getInstance(); +const StrayRepository = require('src/seasoned/StrayRepository'); +const strayRepository = new StrayRepository(); + +function verifyStrayController(req, res) { + const id = req.params.strayId; + + strayRepository.verifyStray(id) + .then(() => { + res.send({ success: true, message: 'Episode verified' }); + }) + .catch((error) => { + res.status(500).send({ success: false, error: error.message }); + }); +} + +module.exports = verifyStrayController; \ No newline at end of file diff --git a/src/webserver/server.js b/src/webserver/server.js new file mode 100644 index 0000000..f36f802 --- /dev/null +++ b/src/webserver/server.js @@ -0,0 +1,8 @@ +const config = require('src/config/configuration').getInstance(); +const app = require('./app'); + +module.exports = app.listen(config.get('webserver', 'port'), () => { + console.log('seasonedAPI'); + console.log(`Database is located at ${config.get('database', 'host')}`); + console.log(`Webserver is listening on ${config.get('webserver', 'port')}`); +}) \ No newline at end of file