├── .gitignore
├── lib
├── context-processors
│ ├── index.js
│ └── pathAware.js
├── constants.js
├── utils.js
├── templatingEngine.js
├── server.js
└── routes
│ └── route-pattern.js
├── index.js
├── LICENSE
├── package.json
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .idea
3 |
--------------------------------------------------------------------------------
/lib/context-processors/index.js:
--------------------------------------------------------------------------------
1 | const pathAware = require('./pathAware');
2 |
3 | /**
4 | * @type {{pathAware: Object}}
5 | */
6 | module.exports = {
7 | pathAware
8 | };
9 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /**
3 | * @fileOverview Nova Server.
4 | * @author Pablo Alecio
5 | */
6 | const server = require('./lib/server');
7 | const contextProcessors = require('./lib/context-processors');
8 |
9 | /**
10 | * @type {{server: {start: Function}, contextProcessors: {pathAware: Object}}}
11 | */
12 | module.exports = {
13 | server,
14 | contextProcessors
15 | };
16 |
--------------------------------------------------------------------------------
/lib/constants.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview Nova constants.
3 | * @author Pablo Alecio
4 | */
5 | 'use strict';
6 |
7 | /**
8 | * Exports a frozen object with the project constants.
9 | * @module lib/constants
10 | * @type {Object}
11 | */
12 | module.exports = Object.freeze({
13 | BLANK: '',
14 | ASTERISK: '*',
15 | HTML: 'html',
16 | JSON_TYPE: 'json',
17 | TEXT_TYPE: 'text/html'
18 | });
19 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Outputs args to the console, adding '> Nova Server:' as prefix.
5 | * @param args The arguments to log.
6 | */
7 | const log = (...args) => console.log('> Nova Server:', ...args);
8 |
9 | /**
10 | * Deep clones obj and returns the clone.
11 | * @param obj The object to clone
12 | */
13 | const cloneObject = obj =>
14 | (typeof obj === 'object' && JSON.parse(JSON.stringify(obj))) || null;
15 |
16 | module.exports = {
17 | log,
18 | cloneObject
19 | };
20 |
--------------------------------------------------------------------------------
/lib/templatingEngine.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview Contains the logic for the Templating Engine.
3 | * @author Pablo Alecio
4 | */
5 | 'use strict';
6 |
7 | const handlebars = require('handlebars');
8 |
9 | /**
10 | * Compiles the markup using the contentModel
11 | * @param {string} markup
12 | * @param {Object} contentModel
13 | * @returns {string} the compiled markup
14 | */
15 | const compileTemplate = (markup, contentModel) => {
16 | const template = handlebars.compile(markup);
17 | return template(contentModel);
18 | };
19 |
20 | /**
21 | * @module lib/templatingEngine
22 | * @type { compileTemplate }
23 | */
24 | module.exports = { compileTemplate };
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020 Pablo Alecio
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@palecio/nova-server",
3 | "version": "1.5.0",
4 | "description": "Lightweight web server using Expressjs that enables building web APIs with Nova.",
5 | "main": "index.js",
6 | "scripts": {},
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/PaleSC2/nova-server.git"
10 | },
11 | "keywords": [
12 | "nova",
13 | "server",
14 | "webserver",
15 | "rest",
16 | "api",
17 | "expressjs",
18 | "microservices"
19 | ],
20 | "engines": {
21 | "node": ">=12.11.0",
22 | "npm": ">=6.11.3"
23 | },
24 | "author": {
25 | "name": "Pablo Alecio",
26 | "email": "paleciop@gmail.com"
27 | },
28 | "license": "MIT",
29 | "bugs": {
30 | "url": "https://github.com/PaleSC2/nova-server/issues"
31 | },
32 | "homepage": "",
33 | "dependencies": {
34 | "@palecio/nova-core": "^1.4.1",
35 | "axios": "^0.19.2",
36 | "body-parser": "^1.17.2",
37 | "express": "^4.15.4",
38 | "handlebars": "^4.7.6"
39 | },
40 | "prettier": {
41 | "singleQuote": true
42 | },
43 | "devDependencies": {
44 | "nodemon": "^2.0.4",
45 | "prettier": "^1.19.1"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Nova Server
2 | Lightweight web server using Expressjs that enables building web APIs with Nova.
3 |
4 | ## Install
5 |
6 | ```
7 | npm install @palecio/nova-server
8 | ```
9 |
10 | ## Create your own Nova server
11 | ```
12 | const serverConfig = {
13 | contextProcessorPaths: '',
14 | port: , //Default is 9001
15 | baseURLPath: , //Default is '/api'
16 | allowedHostnames: , // hostnames allowed to make cross domain requests
17 | baseContentModel: { // The base content model
18 | config: {} //an object with configs that will be accessible to the Context Processors
19 | }
20 | };
21 |
22 | server.start(serverConfig).then(() => {
23 | console.log('Nova Server Running!');
24 | });
25 | ```
26 |
27 | ## Example Context Processor
28 | ```
29 | const contextProcessor = require("nova-core").pathAwareContextProcessor;
30 |
31 | module.exports = contextProcessor.extend({
32 | patterns: ["/my-path"], //Express-like routes
33 | priority: 10, //The higher the number, the sooner it runs, no priority means the CP doesn't have any dependencies so it'll run in parallel
34 | process(executionContext, contentModel) {
35 | //do something with the content model here
36 | }
37 | });
38 | ```
39 |
40 | ### execution context TODO
41 | ### access request TODO
42 | ### httpHeaders context TODO
43 | ### templatingEngine TODO
44 | ### debugMode TODO
45 |
--------------------------------------------------------------------------------
/lib/context-processors/pathAware.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview Path Aware Context Processor, meant to be extended with {@link extend}
3 | * @author Pablo Alecio
4 | */
5 | 'use strict';
6 |
7 | const RoutePattern = require('../routes/route-pattern');
8 | const contextProcessor = require('@palecio/nova-core').contextProcessor;
9 |
10 | /**
11 | * @module lib/context-processors/pathAwareContextProcessor
12 | * @type {Object}
13 | */
14 | module.exports = contextProcessor.extend({
15 | /**
16 | * The context processor name.
17 | */
18 | name: 'Path Aware',
19 | /**
20 | * A list of URL patterns which the request path has to match in order for this Context Processor to be executed.
21 | */
22 | patterns: [],
23 | /**
24 | * Gets the pattern that matches thePath.
25 | * @param thePath the path.
26 | * @returns {string} The pattern that matched the path.
27 | */
28 | getMatchingPattern(thePath) {
29 | for (let i = 0; i < this.patterns.length; i++) {
30 | const routePattern = RoutePattern.fromString(this.patterns[i]);
31 | const match = routePattern.matches(thePath);
32 | if (match) {
33 | return this.patterns[i];
34 | }
35 | }
36 | return '';
37 | },
38 | /**
39 | * Gets the path from executionContext. First, it looks for a path property, if that fails, it uses the request path.
40 | * @param {Object} executionContext The Execution Context.
41 | * @returns {string} the path of the execution context.
42 | */
43 | getPath(executionContext) {
44 | return (
45 | executionContext.path ||
46 | (executionContext.request && executionContext.request.baseUrl) ||
47 | ''
48 | );
49 | },
50 | /**
51 | * Gets the match object containing the variables which matched the path.
52 | * @param executionContext The Execution Context.
53 | * @returns {Object} The match.
54 | */
55 | getMatch(executionContext) {
56 | const thePath = this.getPath(executionContext);
57 | const pattern = this.getMatchingPattern(thePath);
58 | const routePattern = RoutePattern.fromString(pattern);
59 | return routePattern.match(thePath);
60 | },
61 | /**
62 | * Checks if this Context Processor has to be executed based on the path in the execution context.
63 | * @param {Object} executionContext The execution context.
64 | * @returns {boolean} True if the path sent as argument matches any of the patterns; false otherwise.
65 | */
66 | accepts(executionContext) {
67 | const thePath = this.getPath(executionContext);
68 | return !!this.getMatchingPattern(thePath);
69 | }
70 | });
71 |
--------------------------------------------------------------------------------
/lib/server.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview Nova Server.
3 | * @author Pablo Alecio
4 | */
5 | 'use strict';
6 |
7 | const express = require('express');
8 | const bodyParser = require('body-parser');
9 | const nova = require('@palecio/nova-core');
10 | const templatingEngine = require('./templatingEngine');
11 |
12 | const { ASTERISK, HTML, JSON_TYPE, TEXT_TYPE } = require('./constants');
13 | const utils = require('./utils');
14 |
15 | const app = express();
16 | const router = express.Router();
17 | const { log, cloneObject } = utils;
18 | const DEFAULT_NOVA_PORT = 9001;
19 | const DEFAULT_BASE_URL_PATH = '/api';
20 |
21 | /**
22 | * @async
23 | * @param configuration {Object} the server configuration object
24 | */
25 | const start = async function start(configuration = {}) {
26 | const {
27 | port = DEFAULT_NOVA_PORT, // The port where the express server will start
28 | baseURLPath = DEFAULT_BASE_URL_PATH, // The route URL prefix. Default is '/api'
29 | allowedHostnames = ['localhost'], // The host names that are allowed to make requests to this server
30 | contextProcessors = {}, // An array of literal Context Processors or a single literal Context Processor
31 | contextProcessorPaths = [], // An array of paths or a single path where the Context Processors are located in the file system
32 | baseContentModel = {} // The starting Content Model
33 | } = configuration;
34 |
35 | const paths = Array.isArray(contextProcessorPaths)
36 | ? contextProcessorPaths
37 | : [contextProcessorPaths];
38 |
39 | /**
40 | * The Context Processor engine
41 | * @async
42 | * @type {Function}
43 | */
44 | const cpe = await nova.fetchContextProcessorEngine({
45 | contextProcessors,
46 | paths
47 | });
48 |
49 | /**
50 | * A function that executes the Context Processors based on the data in the execution context
51 | * @async
52 | * @param executionContext the Execution Context
53 | * @param contentModel the Content Model
54 | * @returns {Promise