Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .env.example
Empty file.
25 changes: 25 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
language: node_js
node_js:
- 'lts/*'

install:
- yarn install

services: mongodb

cache:
yarn: true
directories:
- node_modules

before_script:
- sleep 15
- mongo mydb_test --eval 'db.createUser({user:"travis",pwd:"test",roles:["readWrite"]});'

scripts:
- yarn build
- yarn test
- yarn test:cov

after_success:
- yarn coveralls
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# support
# support

[![Build Status](https://www.travis-ci.com/dbytecoderc/support.svg?branch=main)](https://www.travis-ci.com/dbytecoderc/support) [![Coverage Status](https://coveralls.io/repos/github/dbytecoderc/support/badge.svg?branch=main)](https://coveralls.io/github/dbytecoderc/support?branch=main)
35 changes: 35 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module.exports = {
roots: ['src'],
globals: {
'ts-jest': {
tsconfig: 'tsconfig.json',
},
},
moduleFileExtensions: ['ts', 'js'],
transform: {
'^.+\\.ts?$': 'ts-jest',
},
testRegex: '.spec.ts$',
modulePathIgnorePatterns: ['__mocks__', 'node_modules'],
testEnvironment: 'node',
coverageDirectory: './coverage',
collectCoverage: true,
collectCoverageFrom: [
'**/*.{js,jsx,ts,tsx}',
'!**/@types/**',
'!**/interfaces/**',
'!**/migrations/**',
'!**/node_modules/**',
'!**/dist/**',
'!**/coverage/**',
'!**/typings/**',
'!src/index.ts',
'!src/app.ts',
'!src/database/index.ts',
'!src/modules/app/**',
'!src/config/**',
],
coverageReporters: ['lcov', 'text'],
testPathIgnorePatterns: ['<rootDir>/dist/', '<rootDir>/node_modules/'],
testTimeout: 30000,
};
63 changes: 63 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"name": "support",
"version": "1.0.0",
"description": "The system allows customers to be able to place support requests, and support agents to process the request.",
"main": "index.js",
"scripts": {
"start": "node ./dist/src/index.js",
"build": "yarn clean && tsc ",
"start:dev": "cross-env NODE_ENV=development ts-node-dev --files --debug --clear ./src/index.ts",
"clean": "rm -rf dist && mkdir dist",
"prestart:prod": "yarn build",
"start:prod": "cross-env NODE_ENV=staging node ./dist/src/index.js",
"test": "cross-env NODE_ENV=test PORT=3000 jest --no-cache --detectOpenHandles --runInBand --forceExit --verbose",
"test:watch": "cross-env NODE_ENV=test PORT=3000 jest --runInBand --detectOpenHandles --watch",
"test:cov": "cross-env NODE_ENV=test PORT=3000 jest --no-cache --detectOpenHandles --runInBand --forceExit --coverage",
"ts-watch": "yarn clean && tsc -w",
"coveralls": "jest --coverage --coverageReporters=text-lcov | coveralls"
},
"repository": {
"type": "git",
"url": "git+https://github.com/dbytecoderc/support.git"
},
"author": "DC",
"license": "ISC",
"bugs": {
"url": "https://github.com/dbytecoderc/support/issues"
},
"homepage": "https://github.com/dbytecoderc/support#readme",
"dependencies": {
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"mongodb": "^3.6.6",
"mongoose": "^5.12.3",
"typescript": "^4.2.4"
},
"devDependencies": {
"@types/app-root-path": "^1.2.4",
"@types/bcryptjs": "^2.4.2",
"@types/cors": "^2.8.10",
"@types/express": "^4.17.11",
"@types/jest": "^26.0.22",
"@types/jsonwebtoken": "^8.5.1",
"@types/morgan": "^1.9.2",
"@types/node": "^14.14.37",
"@types/supertest": "^2.0.11",
"app-root-path": "^3.0.0",
"codecov": "^3.8.1",
"coveralls": "^3.1.0",
"jest": "^26.6.3",
"morgan": "^1.10.0",
"nodemon": "^2.0.7",
"supertest": "^6.1.3",
"ts-jest": "^26.5.4",
"ts-node": "^9.1.1",
"ts-node-dev": "^1.1.6",
"tslint": "^6.1.3",
"winston": "^3.3.3"
}
}
8 changes: 8 additions & 0 deletions src/@types/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface Environment {
NODE_ENV: string;
REDIS_URL: string;
PORT: number | null;
APP_SECRET: string;
MONGODB_URI: string,
MONGO_URI_TEST: string,
}
54 changes: 54 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Package Imports
import dotenv from "dotenv";
dotenv.config();
import express, { Application } from "express";
import cors from "cors";
import morgan from "morgan";

// File Imports
import { stream } from "./config/logger";
import modules from "./modules";
import dbconnect from "./config/connection";
import { env } from "./config";
import logger from "./config/logger";

const app: Application = express();

app.use(cors());
app.use(morgan("combined", { stream }));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// API routes
modules(app);

// catch all routers
app.use("*", (req, res) => {
res.status(404).json({
message: "Not Found. Use /api/{app version} to access the Api",
});
});

dbconnect().then(async () => {
// if (process.env.NODE_ENV !== 'test') {
// await seedData();
// }

logger.info(
`Server running on ${process.env.NODE_ENV} environment, on port ${
env.PORT || 5000
}`
);

// if (!module.parent) {
// app.listen(env.PORT, () => {
// logger.info(
// `Server running on ${process.env.NODE_ENV} environment, on port ${
// env.PORT || 5000
// }`
// );
// });
// }
});

export default app;
41 changes: 41 additions & 0 deletions src/config/connection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { connect, connection, Mongoose } from "mongoose";
// import { MongoError } from 'mongodb';
import dotenv from "dotenv";
import logger from "./logger";
import { env } from "./env";

dotenv.config();

const { MONGODB_URI, MONGO_URI_TEST, NODE_ENV } = env;

const dbUrl: string = NODE_ENV === "test" ? MONGO_URI_TEST : MONGODB_URI;

connection.on("connected", () => {
logger.info("Mongoose default connection is open");
});

connection.on("error", (err: Error) => {
logger.info(err.message, err.stack);
});

connection.on("disconnected", () => {
logger.info("Mongoose default connection is disconnected");
});

process.on("SIGINT", async () => {
connection.close(() => {
logger.info(
"Mongoose default connection is disconnected due to application termination"
);
process.exit(0);
});
});

const options = {
useUnifiedTopology: true,
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
};

export default async () => (await connect(dbUrl, options)) as Mongoose;
14 changes: 14 additions & 0 deletions src/config/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as dotenv from "dotenv";
import * as path from "path";
import { Environment } from "../@types/env";

dotenv.config({ path: path.join(__dirname, "../../.env") });

export const env = {
NODE_ENV: process.env.NODE_ENV,
REDIS_URL: process.env.REDIS_URL,
PORT: process.env.PORT || null,
APP_SECRET: process.env.APP_SECRET,
MONGODB_URI: process.env.MONGODB_URI,
MONGO_URI_TEST: process.env.MONGO_URI_TEST,
} as Environment;
2 changes: 2 additions & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './env';
export * from './connection';
37 changes: 37 additions & 0 deletions src/config/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use strict";

import appRoot from "app-root-path";
import * as winston from "winston";

const LOG_FILE_PATH = `${appRoot}/logs/app.log`;
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const MAX_FILES = 5;

// define the custom settings for each transport (file, console)
const options: winston.LoggerOptions = {
silent: process.env.NODE_ENV === 'test' ? true : false,
transports: [
new winston.transports.Console({
level: process.env.NODE_ENV === "production" ? "error" : "debug",
handleExceptions: true,
}),
new winston.transports.File({
filename: LOG_FILE_PATH,
level: "debug",
maxsize: MAX_FILE_SIZE,
maxFiles: MAX_FILES,
handleExceptions: true,
}),
],
};

// instantiate a new Winston Logger with the settings defined above
let logger = winston.createLogger(options);

export const stream = {
write: (message: any) => {
logger.info(message);
},
};

export default logger;
30 changes: 30 additions & 0 deletions src/database/models/User.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { model, Schema } from "mongoose";

import { User } from "../../interfaces/user";

// Create Schema
const UserSchema = new Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
admin: {
type: Boolean,
default: false,
},
});

export default model<User>("User", UserSchema);
47 changes: 47 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import dotenv from "dotenv";
dotenv.config();
import http from "http";
import { AddressInfo } from "net";
import chalk from "chalk";
import { env } from "./config";
import app from "./app";
import logger from "./config/logger";

export const server: http.Server = http.createServer(app);

const port: number | null = env.PORT || 8080;

function onError(error: NodeJS.ErrnoException): void {
if (error.syscall !== "listen") {
throw error;
}

const bind = typeof port === "string" ? "Pipe " + port : "Port " + port;

switch (error.code) {
case "EACCES":
logger.error(`${bind} requires elevated privileges`);
process.exit(1);
case "EADDRINUSE":
logger.error(`${bind} is already in use`);
process.exit(1);
default:
throw error;
}
}

function onListening(): void {
const address = server.address() as AddressInfo;
const bind =
typeof address === "string" ? "pipe " + address : "port " + address.port;
logger.info(chalk.bold.green("Server is listening on", bind));
}

server.on("error", onError);
server.on("listening", onListening);

server.listen(port, () => {
logger.info(`Find me on http://localhost:${port}`);
});

export default server;
15 changes: 15 additions & 0 deletions src/interfaces/user.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Document } from "mongoose";

export interface CreateUserInput {
name: string;
email: string;
password: string;
}

export interface User extends Document {
name: string;
email: string;
password: string;
createdAt: Date;
admin: boolean;
}
Loading