-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c63a40c
Showing
36 changed files
with
1,139 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
root = true | ||
[*] | ||
end_of_line = crlf | ||
indent_style = space | ||
indent_size = 2 | ||
charset = utf-8 | ||
trim_trailing_whitespace = false | ||
insert_final_newline = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
PORT=3000 | ||
NAME_API=API | ||
|
||
PGUSER=user | ||
PGHOST=localhost | ||
PGPASSWORD=pass | ||
PGDATABASE=name | ||
PGPORT=5432 | ||
|
||
EMAILUSER=user | ||
EMAILPASSWORD=pass | ||
EMAILHOST=smtp.mailtrap.io | ||
EMAILPORT=2525 | ||
|
||
URL_CLIENT=https://client.io | ||
|
||
DATABASE_MONGO=mongodb+srv://user:[email protected]/database | ||
|
||
SSL=false | ||
|
||
LOG_DIR=./logs | ||
|
||
SECRETKEY=sdfsd&%efgewr32 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
node_modules/ | ||
package-lock.json | ||
.env | ||
build | ||
production | ||
logs/ | ||
coverage/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
image: node:12.19.0 | ||
|
||
stages: | ||
- install | ||
- test | ||
- build | ||
- deploy | ||
|
||
install: | ||
stage: install | ||
script: | ||
- npm install | ||
artifacts: | ||
expire_in: 1h | ||
paths: | ||
- node_modules/ | ||
cache: | ||
paths: | ||
- node_modules/ | ||
|
||
tests: | ||
stage: test | ||
dependencies: | ||
- install | ||
script: | ||
- npm run test | ||
|
||
build: | ||
stage: build | ||
dependencies: | ||
- install | ||
script: | ||
- npm run build | ||
artifacts: | ||
expire_in: 1h | ||
paths: | ||
- production/ | ||
only: | ||
- master | ||
|
||
Deploy: | ||
image: ruby:latest | ||
stage: deploy | ||
dependencies: | ||
- build | ||
only: | ||
- master | ||
script: | ||
- apt-get update -qy | ||
- apt-get install -y ruby-dev | ||
- gem install dpl | ||
- dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_API_KEY |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module.exports = { | ||
preset: 'ts-jest', | ||
testEnvironment: 'node', | ||
roots: ['<rootDir>/tests'], | ||
setupFiles: ['<rootDir>/tests/setup.ts'], | ||
collectCoverageFrom: ['<rootDir>/src/**/*.ts', '!**/node_modules/**'] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
{ | ||
"name": "backend-architecture-nodejs", | ||
"version": "1.0.0", | ||
"author": "Nestor Cortina", | ||
"description": "", | ||
"main": "server.js", | ||
"engines": { | ||
"node": "12.19.0", | ||
"npm": "6.14.8", | ||
"typescript": "4.0.3" | ||
}, | ||
"scripts": { | ||
"start": "node ./build/server.js", | ||
"dev": "ts-node-dev src/bin/www.ts", | ||
"test": "jest --forceExit --detectOpenHandles --coverage --verbose", | ||
"prebuild": "rm -rf ./prebuild && tsc", | ||
"deletedev": "rm -rf ./prebuild", | ||
"build": "npm run prebuild && npm run overbuild", | ||
"overbuild": "rm -rf build && webpack --config webpack/webpack.config.js -p --env=prod && npm run deletedev" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "" | ||
}, | ||
"license": "ISC", | ||
"dependencies": { | ||
"bcrypt": "^5.0.0", | ||
"compression": "^1.7.4", | ||
"cors": "^2.8.5", | ||
"express": "^4.17.1", | ||
"helmet": "^4.1.1", | ||
"joi": "^17.2.1", | ||
"jsonwebtoken": "^8.5.1", | ||
"lodash": "^4.17.20", | ||
"mongoose": "^5.10.5", | ||
"morgan": "^1.10.0", | ||
"nodemailer": "^6.4.14", | ||
"pg": "^8.3.3" | ||
}, | ||
"devDependencies": { | ||
"@types/compression": "^1.7.0", | ||
"@types/bcrypt": "^3.0.0", | ||
"@types/cors": "^2.8.8", | ||
"@types/express": "^4.17.8", | ||
"@types/http-errors": "^1.8.0", | ||
"@types/jest": "^26.0.15", | ||
"@types/jsonwebtoken": "^8.5.0", | ||
"@types/lodash": "^4.14.162", | ||
"@types/mongoose": "^5.7.36", | ||
"@types/morgan": "^1.9.1", | ||
"@types/node": "^14.14.0", | ||
"@types/nodemailer": "^6.4.0", | ||
"@types/pg": "^7.14.4", | ||
"@types/supertest": "^2.0.10", | ||
"babel-loader": "^8.1.0", | ||
"dotenv": "^8.2.0", | ||
"jest": "^26.6.0", | ||
"supertest": "^5.0.0", | ||
"ts-jest": "^26.4.1", | ||
"ts-node-dev": "^1.0.0", | ||
"typescript": "^4.0.2", | ||
"webpack": "^4.44.1", | ||
"webpack-cli": "^3.3.12", | ||
"webpack-node-externals": "^2.5.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import express, { Request, Response, NextFunction, Application } from 'express' | ||
import compression from 'compression'; | ||
import helmet from 'helmet' | ||
import cors from 'cors'; | ||
import bodyParser from 'body-parser' | ||
import morgan from 'morgan' | ||
|
||
import routesV1 from './routes/v1' | ||
import { template } from './helpers/template' | ||
import { environment } from './config' | ||
import { pool } from './database/pgPool' | ||
import { ApiError, InternalError, NotFoundError } from './core/ApiError' | ||
import db from './database/mongo'; | ||
|
||
process.on('uncaughtException', (e) => { | ||
console.log(e) | ||
}) | ||
|
||
const app: Application = express() | ||
|
||
app.use(cors()); | ||
app.use(helmet()); | ||
app.use(compression()); | ||
|
||
app.use(bodyParser.json({ limit: '10mb' })) | ||
app.use(bodyParser.urlencoded({ limit: '10mb', extended: true, parameterLimit: 50000 })) | ||
|
||
app.use(morgan('dev')) | ||
|
||
app.use('/v1', routesV1) | ||
app.get('/', (req: Request, res: Response) => { | ||
res.status(200).send(template('welcome to api')) | ||
}) | ||
|
||
app.use((req, res, next) => next(new NotFoundError())) | ||
|
||
app.use((err: Error, req: Request, res: Response, next: NextFunction) => { | ||
if (err instanceof ApiError) { | ||
ApiError.handle(err, res) | ||
} else { | ||
if (environment === 'development') { | ||
console.log(err) | ||
return res.status(500).send(err.message) | ||
} | ||
ApiError.handle(new InternalError(), res) | ||
} | ||
}) | ||
|
||
pool.connect() | ||
.then() | ||
.catch((error: any) => console.log(`ERROR: ${error.message || error}`)) | ||
|
||
db.once('open', function () { | ||
console.log('The connection to MongoDB was successful.'); | ||
}); | ||
|
||
export default app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import http from 'http'; | ||
import app from '../app'; | ||
import { port } from '../config' | ||
|
||
app.set('port', port); | ||
|
||
const server = http.createServer(app); | ||
|
||
server.listen(port, () => { | ||
console.log(`server running on port ${port}`) | ||
}) | ||
.on('error', (e) => console.log(e)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
if (!process.env.NODE_ENV) require('dotenv').config() | ||
|
||
export const environment = process.env.NODE_ENV | ||
export const port = process.env.PORT | ||
|
||
export const pgUser = process.env.PGUSER | ||
export const pgPassword = process.env.PGPASSWORD | ||
export const pgHost = process.env.PGHOST | ||
export const pgDatabase = process.env.PGDATABASE | ||
export const pgPort = parseInt(process.env.PGPORT) | ||
export const pgSsl = (process.env.SSL === 'false') ? false : true | ||
|
||
export const emailUser = process.env.EMAILUSER | ||
export const emailPassword = process.env.EMAILPASSWORD | ||
export const emailHost = process.env.EMAILHOST | ||
export const emailPort = process.env.EMAILPORT | ||
|
||
export const urlClient = process.env.URL_CLIENT | ||
|
||
export const secretKey = process.env.SECRETKEY |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { Response } from 'express' | ||
import { environment } from '../config' | ||
import { | ||
AuthFailureResponse, | ||
AccessTokenErrorResponse, | ||
InternalErrorResponse, | ||
NotFoundResponse, | ||
BadRequestResponse, | ||
ForbiddenResponse, | ||
} from './ApiResponse' | ||
|
||
enum ErrorType { | ||
BAD_TOKEN = 'BadTokenError', | ||
TOKEN_EXPIRED = 'TokenExpiredError', | ||
UNAUTHORIZED = 'AuthFailureError', | ||
ACCESS_TOKEN = 'AccessTokenError', | ||
INTERNAL = 'InternalError', | ||
NOT_FOUND = 'NotFoundError', | ||
NO_ENTRY = 'NoEntryError', | ||
NO_DATA = 'NoDataError', | ||
BAD_REQUEST = 'BadRequestError', | ||
FORBIDDEN = 'ForbiddenError', | ||
} | ||
|
||
export abstract class ApiError extends Error { | ||
constructor(public type: ErrorType, public message: string = 'error') { | ||
super(type); | ||
} | ||
|
||
public static handle(err: ApiError, res: Response): Response { | ||
switch (err.type) { | ||
case ErrorType.BAD_TOKEN: | ||
case ErrorType.TOKEN_EXPIRED: | ||
case ErrorType.UNAUTHORIZED: | ||
return new AuthFailureResponse(err.message).send(res); | ||
case ErrorType.ACCESS_TOKEN: | ||
return new AccessTokenErrorResponse(err.message).send(res); | ||
case ErrorType.INTERNAL: | ||
return new InternalErrorResponse(err.message).send(res); | ||
case ErrorType.NOT_FOUND: | ||
case ErrorType.NO_ENTRY: | ||
case ErrorType.NO_DATA: | ||
return new NotFoundResponse(err.message).send(res); | ||
case ErrorType.BAD_REQUEST: | ||
return new BadRequestResponse(err.message).send(res); | ||
case ErrorType.FORBIDDEN: | ||
return new ForbiddenResponse(err.message).send(res); | ||
default: { | ||
let message = err.message; | ||
// Do not send failure message in production as it may send sensitive data | ||
if (environment === 'production') message = 'Something wrong happened.'; | ||
return new InternalErrorResponse(message).send(res); | ||
} | ||
} | ||
} | ||
} | ||
|
||
export class AuthFailureError extends ApiError { | ||
constructor(message = 'Invalid Credentials') { | ||
super(ErrorType.UNAUTHORIZED, message); | ||
} | ||
} | ||
|
||
export class InternalError extends ApiError { | ||
constructor(message = 'Internal error') { | ||
super(ErrorType.INTERNAL, message); | ||
} | ||
} | ||
|
||
export class BadRequestError extends ApiError { | ||
constructor(message = 'Bad Request') { | ||
super(ErrorType.BAD_REQUEST, message); | ||
} | ||
} | ||
|
||
export class NotFoundError extends ApiError { | ||
constructor(message = 'Not Found') { | ||
super(ErrorType.NOT_FOUND, message); | ||
} | ||
} | ||
|
||
export class ForbiddenError extends ApiError { | ||
constructor(message = 'Permission denied') { | ||
super(ErrorType.FORBIDDEN, message); | ||
} | ||
} | ||
|
||
export class NoEntryError extends ApiError { | ||
constructor(message = "Entry don't exists") { | ||
super(ErrorType.NO_ENTRY, message); | ||
} | ||
} | ||
|
||
export class BadTokenError extends ApiError { | ||
constructor(message = 'Token is not valid') { | ||
super(ErrorType.BAD_TOKEN, message); | ||
} | ||
} | ||
|
||
export class TokenExpiredError extends ApiError { | ||
constructor(message = 'Token is expired') { | ||
super(ErrorType.TOKEN_EXPIRED, message); | ||
} | ||
} | ||
|
||
export class NoDataError extends ApiError { | ||
constructor(message = 'No data available') { | ||
super(ErrorType.NO_DATA, message); | ||
} | ||
} | ||
|
||
export class AccessTokenError extends ApiError { | ||
constructor(message = 'Invalid access token') { | ||
super(ErrorType.ACCESS_TOKEN, message); | ||
} | ||
} |
Oops, something went wrong.