├── .editorconfig ├── .env_template ├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── business ├── entities │ └── Message │ │ ├── Message.entity.js │ │ └── MessageCollection.entity.js ├── repositories │ └── Message.repository.js └── use_cases │ └── manageMessages │ ├── createMessage.uc.js │ └── listMessages.uc.js ├── components └── Logger.js ├── infra ├── services │ └── censor.service.js ├── settings.js └── web │ ├── app.js │ ├── babel_hook.js │ ├── messages │ ├── messages.controller.js │ └── messages.routes.js │ └── routes.js └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | [*] 3 | indent_style = space 4 | indent_size = 2 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /.env_template: -------------------------------------------------------------------------------- 1 | NODE_ENV=dev 2 | HTTP_PORT=3000 3 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb/base", 3 | "parser": "babel-eslint", 4 | "ecmaFeatures": { 5 | "classes": true 6 | }, 7 | "rules": { 8 | "comma-dangle": [2, "never"], 9 | "func-names": 0, 10 | "no-param-reassign": 0, 11 | "no-multi-str": 0, 12 | "consistent-return": 0 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directories 27 | node_modules 28 | jspm_packages 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | 36 | .env 37 | .azk 38 | *.pem 39 | .DS_Store 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Scup by Sprinklr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![NodeBase](http://i.imgur.com/mhwQypc.jpg) 2 | 3 | ## NodeBase - Scup NODEJS boilerplate. 4 | 5 | ### Using 6 | 7 | - git clone https://github.com/scup/nodebase.git && cd nodebase 8 | - npm install 9 | - npm start 10 | -------------------------------------------------------------------------------- /business/entities/Message/Message.entity.js: -------------------------------------------------------------------------------- 1 | import Entity from 'speck-entity'; 2 | import React from 'react'; 3 | import MessageRepository from '../../repositories/Message.repository'; 4 | import MessageCollection from './MessageCollection.entity'; 5 | 6 | const messageRepository = new MessageRepository(); 7 | 8 | export default class Message extends Entity { 9 | 10 | static SCHEMA = { 11 | text: React.PropTypes.string, 12 | cleaned: React.PropTypes.bool 13 | } 14 | 15 | save() { 16 | return messageRepository.create(this); 17 | } 18 | 19 | clean() { 20 | return messageRepository.cleanMessage(this); 21 | } 22 | 23 | list() { 24 | return messageRepository.list() 25 | .then(rawMessages => new MessageCollection(rawMessages)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /business/entities/Message/MessageCollection.entity.js: -------------------------------------------------------------------------------- 1 | import Speck from 'speck-entity'; 2 | import Message from '../Message/Message.entity'; 3 | 4 | 5 | export default class MessageCollection extends Speck.SpeckCollection { 6 | static entity = Message; 7 | 8 | fetch() { 9 | return this.items.map(item => item.fetch()); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /business/repositories/Message.repository.js: -------------------------------------------------------------------------------- 1 | import CensorService from '../../infra/services/censor.service'; 2 | 3 | const censorService = new CensorService(); 4 | 5 | export default class MessageRepository{ 6 | 7 | constructor(args) { 8 | this.storage = []; 9 | this.censorService = args && args.censorService || censorService; 10 | } 11 | 12 | create(message) { 13 | this.storage.push(message); 14 | return Promise.resolve(message); 15 | } 16 | 17 | list() { 18 | return Promise.resolve(this.storage); 19 | } 20 | 21 | cleanMessage(message) { 22 | return this.censorService.cleanInsults(message); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /business/use_cases/manageMessages/createMessage.uc.js: -------------------------------------------------------------------------------- 1 | import Message from '../../../business/entities/Message/Message.entity'; 2 | 3 | export default class CreateMessageUseCase { 4 | execute(message) { 5 | const newMessage = new Message({ 6 | text: message 7 | }); 8 | 9 | return newMessage.clean() 10 | .then(cleanedMessage => { 11 | return cleanedMessage.save() 12 | }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /business/use_cases/manageMessages/listMessages.uc.js: -------------------------------------------------------------------------------- 1 | import Message from '../../entities/Message/Message.entity'; 2 | 3 | export default class ListMessagesUseCase { 4 | 5 | execute() { 6 | return (new Message()).list(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /components/Logger.js: -------------------------------------------------------------------------------- 1 | import winston from 'winston'; 2 | 3 | export default new winston.Logger({ 4 | transports: [ 5 | new winston.transports.Console({ 6 | json: true, 7 | timestamp: true, 8 | stringify: true 9 | }) 10 | ] 11 | }); 12 | -------------------------------------------------------------------------------- /infra/services/censor.service.js: -------------------------------------------------------------------------------- 1 | import request from 'request'; 2 | 3 | export default class CensorService { 4 | constructor() { 5 | this.host = 'http://www.purgomalum.com/service/json?text='; 6 | } 7 | 8 | cleanInsults(message) { 9 | return new Promise((resolve, reject) => 10 | request(this.host + message.text, (err, res, body) => { 11 | if (err) { 12 | return reject(err); 13 | } 14 | const result = JSON.parse(body).result; 15 | message.text = result; 16 | message.cleaned = true; 17 | return resolve(message); 18 | }) 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /infra/settings.js: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | 3 | dotenv.config({ silent: true }); 4 | 5 | const Settings = { 6 | web: { 7 | httpPort: process.env.HTTP_PORT || 3000 8 | } 9 | }; 10 | 11 | export default Settings; 12 | -------------------------------------------------------------------------------- /infra/web/app.js: -------------------------------------------------------------------------------- 1 | import Settings from '../settings'; 2 | import Logger from '../../components/Logger'; 3 | import express from 'express'; 4 | import routes from './routes'; 5 | import bodyParser from 'body-parser'; 6 | import morgan from 'morgan'; 7 | import cors from 'cors'; 8 | 9 | class WebApp { 10 | constructor(app) { 11 | this.app = app; 12 | this.useCors(); 13 | this.parseBody(); 14 | this.morgan(); 15 | this.fetchRoutes(); 16 | this.init(); 17 | } 18 | 19 | parseBody() { 20 | this.app.use(bodyParser.json()); 21 | } 22 | 23 | morgan() { 24 | this.app.use(morgan('tiny', { stream: { write: Logger.info } })); 25 | } 26 | 27 | useCors() { 28 | this.app.use(cors()); 29 | } 30 | 31 | fetchRoutes() { 32 | return routes(this.app); 33 | } 34 | 35 | init() { 36 | return this.app.listen(Settings.web.httpPort, this.banner); 37 | } 38 | 39 | banner() { 40 | console.log(`Server UP and Running in port: ${Settings.web.httpPort}`); 41 | } 42 | } 43 | 44 | export const app = new WebApp(express()); 45 | -------------------------------------------------------------------------------- /infra/web/babel_hook.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('babel-core/register')({ 4 | presets: ['es2015', 'stage-0', 'stage-1'], 5 | plugins: ['transform-class-properties'] 6 | }); 7 | require('./app'); 8 | -------------------------------------------------------------------------------- /infra/web/messages/messages.controller.js: -------------------------------------------------------------------------------- 1 | import ListMessagesUseCase from '../../../business/use_cases/manageMessages/listMessages.uc'; 2 | import CreateMessageUseCase from '../../../business/use_cases/manageMessages/createMessage.uc'; 3 | 4 | 5 | export default class MessagesController { 6 | list() { 7 | return new ListMessagesUseCase().execute(); 8 | } 9 | 10 | insert(req) { 11 | return new CreateMessageUseCase().execute(req.body.message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /infra/web/messages/messages.routes.js: -------------------------------------------------------------------------------- 1 | import r from 'ressendr'; 2 | 3 | import Speck from 'speck-entity'; 4 | 5 | r.addCustomHandler(v => { 6 | return v instanceof Speck || v instanceof Speck.SpeckCollection; 7 | },(v, res)=>{ 8 | res.setHeader('content-type', 'text/javascript'); 9 | return v.fetch(); 10 | }) 11 | 12 | import MessagesController from './messages.controller'; 13 | const controller = new MessagesController(); 14 | 15 | export default function (app) { 16 | app.get('/messages', r.handle(controller.list)); 17 | app.post('/messages', r.handle(controller.insert)); 18 | app.get('/hello', r.handle(controller.hello)); 19 | return app; 20 | } 21 | -------------------------------------------------------------------------------- /infra/web/routes.js: -------------------------------------------------------------------------------- 1 | import messagesRoutes from './messages/messages.routes'; 2 | 3 | const modules = [messagesRoutes]; 4 | 5 | export default function (app) { 6 | modules.map((module) => module(app)); 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodebase", 3 | "version": "1.0.0", 4 | "description": "README.md", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "nodemon ./infra/web/babel_hook.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "babel": "^6.5.2", 14 | "babel-core": "^6.10.4", 15 | "babel-plugin-transform-class-properties": "^6.10.2", 16 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 17 | "babel-preset-es2015": "^6.9.0", 18 | "body-parser": "^1.15.0", 19 | "cors": "^2.7.1", 20 | "dotenv": "^2.0.0", 21 | "express": "4.13.4", 22 | "lodash": "^4.13.1", 23 | "morgan": "^1.7.0", 24 | "nodemon": "^1.9.1", 25 | "react": "^15.2.0", 26 | "request": "^2.69.0", 27 | "request-promise": "^2.0.1", 28 | "ressendr": "0.0.57", 29 | "speck-entity": "0.0.4", 30 | "winston": "^2.2.0" 31 | }, 32 | "engines": { 33 | "node": "5.6.0" 34 | }, 35 | "devDependencies": { 36 | "babel-eslint": "^6.0.0-beta.6", 37 | "babel-preset-stage-0": "^6.5.0", 38 | "babel-preset-stage-1": "^6.5.0", 39 | "eslint": "^2.3.0", 40 | "eslint-config-airbnb": "^6.1.0" 41 | } 42 | } 43 | --------------------------------------------------------------------------------