Added basic private
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -59,3 +59,5 @@ typings/
|
||||
|
||||
dist/
|
||||
/package-lock.json
|
||||
/dist
|
||||
/private/data
|
||||
|
@ -5,7 +5,7 @@
|
||||
"main": "private/index.js",
|
||||
"scripts": {
|
||||
"serve": "webpack-serve --config ./webpack.config.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "jest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -24,7 +24,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/YourWishes/domsPlaceNew#readme",
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"pg-promise": "^8.4.4",
|
||||
"react": "^16.4.0",
|
||||
"react-dom": "^16.4.0",
|
||||
"react-helmet": "^5.2.0",
|
||||
@ -38,6 +38,7 @@
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"css-loader": "^0.28.11",
|
||||
|
83
private/app/App.js
Normal file
83
private/app/App.js
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2018 Dominic Masters
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
const
|
||||
ConfigurationManager = require('./../configuration/ConfigurationManager'),
|
||||
DatabaseConnection = require('./../database/DatabaseConnection'),
|
||||
Server = require('./../server/Server')
|
||||
;
|
||||
|
||||
class App {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
getConfig() { return this.config; }
|
||||
getDatabase() { return this.db; }
|
||||
|
||||
//Primary Functions
|
||||
async start() {
|
||||
//First, load our configuration.
|
||||
try {
|
||||
console.log("Loading Configuration...");
|
||||
this.config = new ConfigurationManager(this);
|
||||
this.config.loadConfig();
|
||||
console.log("...Done!");
|
||||
} catch(e) {
|
||||
console.error("Failed to read config!");
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
//Next, connect to the database.
|
||||
try {
|
||||
console.log("Connecting to database...");
|
||||
this.db = new DatabaseConnection(this);
|
||||
this.db.loadQueries();//Load our prepared queries
|
||||
await this.db.connect();//Connect to the DB.
|
||||
console.log("...Done!");
|
||||
} catch(e) {
|
||||
console.error("Failed to connect to the database!");
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
//Now we need to start the server. This provides both a nice interface, as
|
||||
//well as our API Handler (including 2auth callback urls)
|
||||
try {
|
||||
console.log("Starting Server...");
|
||||
this.server = new Server(this);
|
||||
await this.server.start();
|
||||
console.log("...Done!");
|
||||
} catch(e) {
|
||||
console.error("Failed to start the server!");
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Database Specific
|
||||
onDatabaseConnected(db) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = App;
|
87
private/configuration/ConfigurationManager.js
Normal file
87
private/configuration/ConfigurationManager.js
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2018 Dominic Masters
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
//Imports
|
||||
const fs = require('fs');//Used for file based configurations (local testing)
|
||||
const CONFIG_PATH = './private/data/config.json';// TODO: Set this a different way?
|
||||
|
||||
//Constructor
|
||||
class ConfigurationManager {
|
||||
constructor(app) {
|
||||
this.app = app;
|
||||
this.data = {};
|
||||
}
|
||||
|
||||
loadConfig() {
|
||||
//Is this a Heroku Server? I need a nicer way of doing this in the future
|
||||
let process_variables = process.env;
|
||||
this.isHeroku = false;
|
||||
if(
|
||||
process_variables !== typeof undefined &&
|
||||
typeof process_variables.NODE_HOME !== typeof undefined &&
|
||||
process_variables.NODE_HOME.indexOf("heroku") !== -1
|
||||
) {
|
||||
this.isHeroku = true;
|
||||
}
|
||||
|
||||
//Read config data
|
||||
if(!this.isHeroku) {
|
||||
//TODO: Rather than use readSync, convert the whole function to async and use a library like fs-extra for async?
|
||||
let dataRaw = fs.readFileSync(CONFIG_PATH, 'utf8');
|
||||
let data = JSON.parse(dataRaw);
|
||||
if(!data) throw new Error("Failed to parse Config JSON! Check for an error and try again.");
|
||||
this.data = data;
|
||||
} else {
|
||||
this.data = process_variables;
|
||||
}
|
||||
}
|
||||
|
||||
getValueOf(key) {
|
||||
if(this.isHeroku) {
|
||||
key = key.replace(/\./, '_').toUpperCase();
|
||||
if(typeof this.data[key] === typeof undefined) return null;
|
||||
return this.data[key];
|
||||
}
|
||||
return this.getValueOfRecursive(key.split("."));
|
||||
}
|
||||
|
||||
getValueOfRecursive(key_array, data_obj) {
|
||||
if(typeof data_obj === typeof undefined) data_obj = this.data;
|
||||
if(typeof data_obj === typeof undefined) return null;
|
||||
|
||||
let k = key_array[0];
|
||||
let o = data_obj[k];
|
||||
if(typeof o === typeof undefined) return null;
|
||||
|
||||
//Awesome
|
||||
if(key_array.length > 1) {
|
||||
if(typeof o !== typeof {}) return null;
|
||||
key_array.shift();
|
||||
return this.getValueOfRecursive(key_array, o);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ConfigurationManager;//Export
|
126
private/database/DatabaseConnection.js
Normal file
126
private/database/DatabaseConnection.js
Normal file
@ -0,0 +1,126 @@
|
||||
// Copyright (c) 2018 Dominic Masters
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
const
|
||||
pgp = require('pg-promise')(),
|
||||
fs = require('fs'),
|
||||
path = require('path')
|
||||
;
|
||||
|
||||
const QUERIES_PATH = 'queries';
|
||||
|
||||
class DatabaseConnection {
|
||||
constructor(app) {
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
getConfig() {return this.app.getConfig();}//Short Hand Method
|
||||
|
||||
getQueriesPath() {
|
||||
return path.join(__dirname, QUERIES_PATH);
|
||||
}
|
||||
|
||||
loadQueries() {
|
||||
//Load Queries
|
||||
let queries = {};
|
||||
|
||||
if(fs.existsSync(this.getQueriesPath())) {
|
||||
let queryFiles = fs.readdirSync(this.getQueriesPath());
|
||||
for(var i = 0; i < queryFiles.length; i++) {
|
||||
let file = queryFiles[i];
|
||||
let x = fs.readFileSync(path.join(this.getQueriesPath(), file), 'utf8');
|
||||
queries[file.replace(".sql", "")] = x;
|
||||
}
|
||||
}
|
||||
|
||||
this.queries = queries;
|
||||
return queries;
|
||||
}
|
||||
|
||||
isConnected() {
|
||||
return typeof this.db !== typeof undefined;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
await this.connectThen();
|
||||
}
|
||||
|
||||
async connectThen() {
|
||||
if(this.isConnected()) return true;
|
||||
|
||||
if(
|
||||
!this.getConfig().getValueOf("database.connection")
|
||||
&& !this.getConfig().getValueOf("database.url")
|
||||
) {
|
||||
throw new Error("Missing DB Credentials.");
|
||||
}
|
||||
|
||||
this.db = pgp(
|
||||
this.getConfig().getValueOf("database.connection") ||
|
||||
this.getConfig().getValueOf("database.url")
|
||||
);
|
||||
|
||||
//Fire the event
|
||||
if(typeof this.app.onDatabaseConnected === "function") {
|
||||
await this.app.onDatabaseConnected(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getQuery(name) {
|
||||
return this.queries[name];
|
||||
}
|
||||
|
||||
//Database Shorthand functions
|
||||
async none(queryName, data) {
|
||||
let q = this.getQuery(queryName);
|
||||
return await this.db.none(q, data);
|
||||
}
|
||||
|
||||
async any(queryName, data) {
|
||||
let q = this.getQuery(queryName);
|
||||
let x = await this.db.any(q, data);
|
||||
return x;
|
||||
}
|
||||
|
||||
async one(queryName, data) {
|
||||
let q = this.getQuery(queryName);
|
||||
let x = await this.db.one(q, data);
|
||||
return x;
|
||||
}
|
||||
|
||||
async oneOrNone(queryName, data) {
|
||||
let q = this.getQuery(queryName);
|
||||
let x = await this.db.oneOrNone(q, data);
|
||||
return x;
|
||||
}
|
||||
|
||||
async query(queryName, data) {
|
||||
let q = this.getQuery(queryName);
|
||||
let x = await this.db.query(q, data);
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = DatabaseConnection;
|
37
private/index.js
Normal file
37
private/index.js
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2018 Dominic Masters
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//Import
|
||||
const App = require('./app/App');
|
||||
|
||||
//Create
|
||||
const app = new App();
|
||||
|
||||
//Create entire app wrapper for safe logging and exiting from crashes etc.
|
||||
(async () => {
|
||||
//Start the app
|
||||
return await app.start();
|
||||
})().then((e) => console.log).catch((e) => {
|
||||
if(!e) return;
|
||||
console.error(e);
|
||||
});
|
0
private/index.test.js
Normal file
0
private/index.test.js
Normal file
206
private/server/Server.js
Normal file
206
private/server/Server.js
Normal file
@ -0,0 +1,206 @@
|
||||
// Copyright (c) 2018 Dominic Masters
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//Imports
|
||||
const
|
||||
http = require('http'),
|
||||
https = require('https'),
|
||||
express = require('express'),
|
||||
bodyParser = require('body-parser'),
|
||||
fs = require('fs')
|
||||
;
|
||||
|
||||
class Server {
|
||||
constructor(app) {
|
||||
this.app = app;
|
||||
|
||||
//Server settings
|
||||
this.ip =
|
||||
app.getConfig().getValueOf("IP") ||
|
||||
app.getConfig().getValueOf("ip") ||
|
||||
app.getConfig().getValueOf("server.ip") ||
|
||||
app.getConfig().ip ||
|
||||
process.env.ip ||
|
||||
process.env.IP ||
|
||||
null
|
||||
;
|
||||
this.port =
|
||||
app.getConfig().getValueOf("PORT") ||
|
||||
app.getConfig().getValueOf("port") ||
|
||||
app.getConfig().getValueOf("server.port") ||
|
||||
app.getConfig().port ||
|
||||
process.env.port ||
|
||||
process.env.PORT ||
|
||||
80
|
||||
;
|
||||
this.apiBase = app.getConfig().getValueOf("apiBase") || "/API/";
|
||||
if(!this.apiBase.endsWith("/")) this.apiBase += "/";
|
||||
|
||||
this.useHTTPS = app.getConfig().getValueOf("ssl") && app.getConfig().getValueOf("ssl.enable")
|
||||
if(this.useHTTPS) {
|
||||
this.portHTTPS = this.config.ssl.port || 443;
|
||||
if(!this.config.ssl.key) {
|
||||
throw new Error("Invalid SSL Key in Server Configuration");
|
||||
}
|
||||
if(!this.config.ssl.cert) {
|
||||
throw new Error("Invalid SSL Cert in Server Configuration");
|
||||
}
|
||||
|
||||
let keyFile = __dirname+'./../'+this.config.ssl.key;
|
||||
let certFile = __dirname+'./../'+this.config.ssl.cert;
|
||||
if(!fs.existsSync(keyFile)) {
|
||||
throw new Error("Key file \"" + keyFile + "\" doesn't exist!");
|
||||
}
|
||||
if(!fs.existsSync(certFile)) {
|
||||
throw new Error("Key file \"" + certFile + "\" doesn't exist!");
|
||||
}
|
||||
|
||||
this.key = fs.readFileSync(keyFile, 'utf8');
|
||||
this.cert = fs.readFileSync(certFile, 'utf8');
|
||||
}
|
||||
|
||||
//Setup the express wrapper.
|
||||
this.app = express();
|
||||
|
||||
//Setup Express Middleware
|
||||
this.app.use(bodyParser.json({
|
||||
type:'application/json' // to support JSON-encoded bodies
|
||||
}));
|
||||
this.app.use(bodyParser.urlencoded({
|
||||
extended: true
|
||||
}));
|
||||
|
||||
//Create our HTTP and (if needed HTTPS) server(s)
|
||||
this.http = http.createServer(this.app);
|
||||
this.http.on('error', this.onServerError.bind(this));
|
||||
|
||||
if(this.isHTTPS()) {
|
||||
if(!this.key) throw new Error("Can't start server, missing SSL Key");
|
||||
if(!this.cert) throw new Error("Can't start server, missing SSL Cert");
|
||||
|
||||
this.https = https.createServer({
|
||||
key: this.key,
|
||||
cert: this.cert
|
||||
}, this.app);
|
||||
this.https.on('error', this.onServerError.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
getConfig() {return this.config;}
|
||||
getIP() {return this.ip; }
|
||||
getPort() {return this.port;}
|
||||
getAPIBase() {return this.apiBase;}
|
||||
isHTTPS() {return this.useHTTPS;}
|
||||
getHTTPSPort() {return this.portHTTPS;}
|
||||
getKey() {return this.key;}
|
||||
getCertificate() {return this.cert;}
|
||||
|
||||
isRunning() {
|
||||
if(typeof this.http !== typeof undefined) {
|
||||
return this.http.listening;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async start() {
|
||||
if(typeof this.startPromise !== typeof undefined) {
|
||||
await this.startPromise();
|
||||
return;
|
||||
}
|
||||
this.startPromise = new Promise(this.startServerPromise.bind(this));//Lazy Programming FTW
|
||||
await this.startPromise;
|
||||
}
|
||||
|
||||
startServerPromise(resolve, reject) {
|
||||
this.startResolve = resolve;
|
||||
this.startReject = reject;
|
||||
|
||||
let options = {
|
||||
host: this.ip,
|
||||
port: this.port
|
||||
};
|
||||
|
||||
//Start the HTTP Server
|
||||
this.http.listen(options, this.onServerStart.bind(this));
|
||||
|
||||
//HTTPS?
|
||||
if(this.https) {
|
||||
this.https.listen(options, this.portHTTPS);
|
||||
}
|
||||
}
|
||||
|
||||
onServerStart() {
|
||||
this.bound = this.http.address();
|
||||
this.startResolve(this);
|
||||
}
|
||||
|
||||
onServerError(e) {
|
||||
console.log("A Server Error occured!");
|
||||
this.startReject(e);
|
||||
this.stop();
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
|
||||
async stop() {
|
||||
if(typeof this.stopPromise !== typeof undefined) {
|
||||
await this.stopPromise;
|
||||
return;
|
||||
}
|
||||
this.stopPromise = new Promse(this.stopPromise.bind(this));
|
||||
await this.stopPromise;
|
||||
delete this.http;
|
||||
delete this.https;
|
||||
delete this.stopPromise;
|
||||
}
|
||||
|
||||
stopPromise(resolve, reject) {
|
||||
this.stopResolve = resolve;
|
||||
this.stopReject = reject;
|
||||
|
||||
try {
|
||||
this.http.close(this.onHTTPClosed.bind(this));
|
||||
} catch(e) {
|
||||
this.stopReject(e);
|
||||
}
|
||||
}
|
||||
|
||||
onHTTPClosed() {
|
||||
if(typeof this.https === typeof undefined) {
|
||||
this.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.https.close(this.onHTTPSClosed.bind(this));
|
||||
} catch(e) {
|
||||
this.stopReject(e);
|
||||
}
|
||||
}
|
||||
|
||||
onHTTPSClosed() {
|
||||
this.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Server;
|
Reference in New Issue
Block a user