commit 5819796c82e31f0de268fedda3a4f274b7cdf6dc Author: Anatolii Okhotnikov Date: Sun Feb 13 06:31:11 2022 +0000 Initial source code import diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..43392b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +package-lock.json +*/upload/* +!*/upload/.gitkeep diff --git a/README.md b/README.md new file mode 100644 index 0000000..5d5ed60 --- /dev/null +++ b/README.md @@ -0,0 +1,103 @@ +# Send SMS + +Simple API gateway to send SMS messages through Clickatell. + +## Description + +Node.js project to utilise PayLid/IRAS registered API account from Clickatell. +Exposes RESTful interface to send SMS messages. + +## Getting Started + +### Dependencies + +* designed to run on CentOS 6.10 & node.js v11.15.0 +* express.js framework +* no database interface since recent pg-native will not compile on CentOS 6.10 + +### Installing + +* git clone git@gitlab.com:paylidproductteam/sms-api.git +* npm install + +### Executing program + +* To run API in development mode, please use "dev" script from package.json +* Start by +``` +npm run dev +``` +### Calling API + +``` +curl -X POST http://10.10.20.101:4100/v1/sms/send \ + -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc0FkbWluIjoidHJ1ZSIsImlhdCI6MTYzMDY3NTIxMH0.9aqqaT1h7k_JUqUT3cwOvB0RvK51sQuuQuw6-o-6ufc' \ + -H 'Content-Type: application/json' \ + -d '{"number":"16784574356","text":"test from SMS API"}' +``` + +### Getting Message Delivery Status +``` +curl -X POST http://10.10.20.101:4100/v1/sms/status \ + -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc0FkbWluIjoidHJ1ZSIsImlhdCI6MTYzMDY3NTIxMH0.9aqqaT1h7k_JUqUT3cwOvB0RvK51sQuuQuw6-o-6ufc' \ + -H 'Content-Type: application/json' \ + -d '{"apiMessageId":"c7bfb6061000c8805e2cde9a5bdc9139"}' +``` + +## Run Under Supervisor + +Append the following configuration to /etc/supervisord.conf +``` +[program:sms_api] +command=/home/developer/oameye/sms-api/start.sh +directory=/home/developer/oameye/sms-api +process_name=%(program_name)s +numprocs=1 +umask=022 +priority=1 +user=oameye +group=dev +autostart=true +autorestart=true +redirect_stderr=true +log_stdout=true +log_stderr=true +stdout_logfile=/var/log/supervisor/sms_api.log +``` +## Run on Windows + +Download https://nssm.cc/release/nssm-2.24.zip and install into node directory. + +Usage https://nssm.cc/usage + +``` +nssm install SmsApiService C:\AutoMedSys\sms-api\start.bat +nssm set SmsApiService AppDirectory C:\AutoMedSys\sms-api +nssm set SmsApiService DisplayName "SMS API Service" +nssm set SmsApiService Description "SMS RESTful API for Clickatell gateway" +nssm set SmsApiService Start SERVICE_AUTO_START +nssm set SmsApiService AppExit Default Restart +nssm set SmsApiService AppRestartDelay 0 +nssm set SmsApiService AppStdout C:\AutoMedSys\sms-api\service.log +nssm set SmsApiService AppStderr C:\AutoMedSys\sms-api\service.log +nssm start SmsApiService +``` + +## Authors + +Contributors names and contact info + +Anatolii Okhotnikov +[@acidumirae](https://gitlab.com/acidumirae) + +## Version History + +* 0.3 + * Windows service startup instruction + * Make middleware optional +* 0.2 + * Business logic for clickatell SMS API send/status + * See [commit change]() or See [release history]() +* 0.1 + * Initial commit with express.js API skeleton + diff --git a/config/dev.json b/config/dev.json new file mode 100644 index 0000000..ff9d586 --- /dev/null +++ b/config/dev.json @@ -0,0 +1,32 @@ +{ + "dev": { + "name": "Paylid SMS API - Development Mode", + "port": 4100, + "mode": "development", + "protocol": "http", + "serverUrl": "localhost", + "serverUrlWebUrlLink": "localhost:4200/", + "database": { + "port": 5432, + "host": "localhost", + "user": "iras", + "password": "iras", + "database": "iras" + }, + "email": { + "host": "smtp.gmail.com", + "port": "587", + "username": "support_test2@paylid.com", + "password": "may12002" + }, + "clickatell": { + "phone_from": "12014925256", + "base_url": "https://api.clickatell.com/rest", + "api_key": "cZcCI12HIYIWa5SXZfYcSFTsFNKh5uw1yn3YXoct38cS54qON2xEJDxEajCPxUDKekKRE2sN9bRT3o.S" + }, + "middleware": { + "enabled": true, + "secret": "secret" + } + } +} \ No newline at end of file diff --git a/config/production.json b/config/production.json new file mode 100644 index 0000000..2bdcd45 --- /dev/null +++ b/config/production.json @@ -0,0 +1,32 @@ +{ + "production": { + "name": "Paylid SMS API - Production Mode", + "port": 4300, + "mode": "production", + "protocol": "http", + "serverUrl": "", + "serverUrlWebUrlLink": "", + "database": { + "port": 5432, + "host": "localhost", + "user": "iras", + "password": "iras", + "database": "iras" + }, + "email": { + "host": "smtp.gmail.com", + "port": "587", + "username": "support@paylid.com", + "password": "may12002" + }, + "clickatell": { + "phone_from": "12014925256", + "base_url": "https://api.clickatell.com/rest", + "api_key": "cZcCI12HIYIWa5SXZfYcSFTsFNKh5uw1yn3YXoct38cS54qON2xEJDxEajCPxUDKekKRE2sN9bRT3o.S" + }, + "middleware": { + "enabled": true, + "secret": "secret" + } + } +} \ No newline at end of file diff --git a/config/staging.json b/config/staging.json new file mode 100644 index 0000000..2367915 --- /dev/null +++ b/config/staging.json @@ -0,0 +1,32 @@ +{ + "staging": { + "name": "Project Name - Staging Mode", + "port": 4500, + "mode": "staging", + "protocol": "http", + "serverUrl": "", + "serverUrlWebUrlLink": "", + "database": { + "port": 5432, + "host": "localhost", + "user": "iras", + "password": "iras", + "database": "iras" + }, + "email": { + "host": "smtp.gmail.com", + "port": "587", + "username": "support_test2@paylid.com", + "password": "may12002" + }, + "clickatell": { + "phone_from": "12014925256", + "base_url": "https://api.clickatell.com/rest", + "api_key": "cZcCI12HIYIWa5SXZfYcSFTsFNKh5uw1yn3YXoct38cS54qON2xEJDxEajCPxUDKekKRE2sN9bRT3o.S" + }, + "middleware": { + "enabled": true, + "secret": "secret" + } + } +} \ No newline at end of file diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..8d9bd98 --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,17 @@ +module.exports = { + apps: [{ + name: 'PROJECT_NAME', + script: "./src/server.js", + instances: 1, + watch: false, + env_dev: { + NODE_ENV: 'development' + }, + env_staging: { + NODE_ENV: 'staging' + }, + env_production: { + NODE_ENV: 'production' + } + }], +}; diff --git a/nodemon.json b/nodemon.json new file mode 100644 index 0000000..55cbc38 --- /dev/null +++ b/nodemon.json @@ -0,0 +1,7 @@ +{ + "ignore": [ + "node_modules/*", + ".tmp/*", + ".git/*" + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9cbc30f --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "demo", + "version": "1.0.0", + "description": "Demo Structure", + "main": "index.js", + "scripts": { + "dev": "cross-env NODE_ENV=dev nodemon --inspect=5001 --config nodemon.js src/server.js", + "staging": "cross-env NODE_ENV=staging nodemon --inspect=5001 --config nodemon.js src/server.js", + "production": "cross-env NODE_ENV=production nodemon --inspect=5001 --config nodemon.js src/server.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "array-refactor": "^1.0.10", + "async": "3.2.0", + "body-parser": "1.19.0", + "chalk": "4.1.0", + "config": "3.3.3", + "cors": "2.8.5", + "ejs": "3.1.5", + "eslint-config-airbnb": "18.2.1", + "express": "4.17.1", + "express-status-monitor": "^1.3.3", + "express-validator": "6.9.2", + "glob": "7.1.6", + "jsonwebtoken": "8.5.1", + "nodemailer": "6.4.17" + }, + "devDependencies": { + "cross-env": "7.0.3", + "eslint": "7.17.0", + "eslint-config-airbnb-base": "14.2.1", + "eslint-config-standard": "16.0.2", + "eslint-plugin-import": "2.22.1", + "eslint-plugin-node": "11.1.0", + "eslint-plugin-promise": "4.2.1", + "eslint-plugin-standard": "5.0.0", + "nodemon": "2.0.7" + } +} diff --git a/src/constant/ErrorData.js b/src/constant/ErrorData.js new file mode 100644 index 0000000..2d67c94 --- /dev/null +++ b/src/constant/ErrorData.js @@ -0,0 +1,117 @@ +// 40X - Client Side Error +// 50X - Server Side Error + +module.exports = { + ERROR_STATUS_ARRAY: [ + { + status: "401", + message: "userId or privateKey is wrong.", + data: "userId or privateKey is wrong." + }, + { + status: "402", + message: "Mandatory Parameter Empty.", + data: "Mandatory Parameter Empty." + }, + { + status: "403", + message: "Mandatory Parameter Missing.", + data: "Mandatory Parameter Missing." + }, + { + status: "404", + message: "User already registered.", + data: "User already registered." + }, + { + status: "405", + message: "Your password is incorrect.", + data: "Your password is incorrect." + }, + { + status: "406", + message: "Please verify your email.", + data: "Please verify your email." + }, + { + status: "407", + message: "User Not Exist.", + data: "User Not Exist." + }, + { + status: "408", + message: "Verification code was valid for 24 hours, Please click on resend.", + data: "Verification code was valid for 24 hours, Please click on resend." + }, + { + status: "409", + message: "Verification code not exists, Please click on resend.", + data: "Verification code not exists, Please click on resend." + }, + { + status: "410", + message: "User blocked by admin.", + data: "User blocked by admin." + }, + { + status: "411", + message: "User already added.", + data: "User already added." + }, + { + status: "412", + message: "Error in password generator.", + data: "Error in password generator." + }, + { + status: "413", + message: "Verification code eror", + data: "verification code are not match please try again." + }, + { + status: "414", + message: "Token Error", + data: "Invalid token found." + }, + { + status: "415", + message: "Token not found", + data: "Token not found in request perameter." + }, + { + status: "416", + message: "User not verified", + data: "Your are not verified." + }, + { + status: "417", + message: "Authentication error.", + data: "You are not authorized to perform this action." + }, + { + status: "501", + message: "Data Not Found.", + data: "Data Not Found." + }, + { + status: "502", + message: "API Error", + data: "Error occured while processing API request." + }, + { + status: "503", + message: "Database error", + data: "Database operation error." + }, + { + status: "504", + message: "Mail error", + data: "Error occure while sending mail." + }, + { + status: "505", + message: "Error in file deleteing", + data: "File deleteing error." + }, + ] +} \ No newline at end of file diff --git a/src/constant/Routes.js b/src/constant/Routes.js new file mode 100644 index 0000000..4ca69ad --- /dev/null +++ b/src/constant/Routes.js @@ -0,0 +1,4 @@ +module.exports = { + SMS: '/v1/sms', + GET_ERROR: '/v1/errors' +} \ No newline at end of file diff --git a/src/cors/mail.js b/src/cors/mail.js new file mode 100644 index 0000000..7a14c77 --- /dev/null +++ b/src/cors/mail.js @@ -0,0 +1,36 @@ +const nodemailer = require("nodemailer"); +const config = require('config').get(mode); + +let sendMail = (to, subject, html) => { + return new Promise((resolve, reject) => { + let transporter = nodemailer.createTransport({ + host: config.email.host, + port: config.email.port, + secure: true, // true for 465, false for other ports + auth: { + user: config.email.username, + pass: config.email.password + } + }); + + let mailOptions = { + from: config.email.username, + to: to, + subject: subject, + html: html, + } + + transporter.sendMail(mailOptions, (error, info) => { + if (error) { + reject("There is error in mail:- " + error); + } else { + console.log("Mail has been sent to", to) + resolve("Mail Sent!!!"); + } + }); + }) +}; + +module.exports = { + sendMail +} diff --git a/src/cors/middleware.js b/src/cors/middleware.js new file mode 100644 index 0000000..6e1cc96 --- /dev/null +++ b/src/cors/middleware.js @@ -0,0 +1,35 @@ +let jwt = require('jsonwebtoken'); +const reqResponse = require('./responseHandler'); +const config = require('config').get(mode); + +module.exports = { + checkToken +} + +function checkToken(req, res, next) { + if (config.middleware.enabled) { + let token = req.headers['x-access-token'] || req.headers['authorization']; + if (token) { + let key = config.middleware.secret; + jwt.verify(token, key, { + ignoreExpiration: true + }, (err, decoded) => { + if (err) { + return res.status(414).send(reqResponse.errorResponse(414)); + } else { + if (key === config.middleware.secret) { + decoded.isAdminUser = false; + } else { + decoded.isAdminUser = true; + } + req.decoded = decoded; + next(); + } + }); + } else { + return res.status(415).send(reqResponse.errorResponse(415)); + } + } else { + next(); + } +} diff --git a/src/cors/responseHandler.js b/src/cors/responseHandler.js new file mode 100644 index 0000000..420f8a5 --- /dev/null +++ b/src/cors/responseHandler.js @@ -0,0 +1,42 @@ +// 4XX status code related to client side error +// 5XX status code related to server side error + +const getErrorStatus = require('../constant/ErrorData'); + +function findErrorMessage(status, errMessage) { + let data = getErrorStatus.ERROR_STATUS_ARRAY.find(v => v.status == status) || { error: 'There must be an error' }; + if (errMessage) { // override default message + data['data'] = errMessage; + } + return data; +} + +/** + * Success Reposnse. + * @param {number} status - Success response status + * @param {string} succMessage - Success response message + * @param {any} data - Success response custom data + */ +let successResponse = (status, succMessage, data) => { + return { + status, + message: succMessage, + data + } +} + +/** + * Error Reposnse. + * @param {Response} res - Send error response + * @param {number} statusCode - Error Status Code + * @param {string} errMessage - Error response message + */ +let errorResponse = (statusCode, errMessage) => { + return findErrorMessage(statusCode, errMessage); +} + + +module.exports = { + errorResponse, + successResponse, +}; diff --git a/src/routes.js b/src/routes.js new file mode 100644 index 0000000..f6fe17d --- /dev/null +++ b/src/routes.js @@ -0,0 +1,8 @@ +const glob = require('glob'); + +module.exports = (app) => { + glob(`${__dirname}/routes/**/*Routes.js`, {}, (er, files) => { + if (er) throw er; + files.forEach((file) => require(file)(app)); + }); +}; diff --git a/src/routes/errorResponse/ErrorResponseController.js b/src/routes/errorResponse/ErrorResponseController.js new file mode 100644 index 0000000..834ae05 --- /dev/null +++ b/src/routes/errorResponse/ErrorResponseController.js @@ -0,0 +1,16 @@ +const reqResponse = require('../../cors/responseHandler'); +const getErrorStatus = require('../../constant/ErrorData'); + +module.exports = { + getAllErrorData: async (req, res) => { + res.status(201).send(reqResponse.successResponse(201, "Fetch All Error Code", getErrorStatus.ERROR_STATUS_ARRAY)); + }, + + getErrorDataByCode: (req, res) => { + let params = req.params; + let findErrorCode = getErrorStatus.ERROR_STATUS_ARRAY.find(c => c.status == params.code); + res.status(201).send(reqResponse.successResponse(201, "Fetch Error Code", findErrorCode)); + } +} + + diff --git a/src/routes/errorResponse/ErrorResponseRoutes.js b/src/routes/errorResponse/ErrorResponseRoutes.js new file mode 100644 index 0000000..ff7ed58 --- /dev/null +++ b/src/routes/errorResponse/ErrorResponseRoutes.js @@ -0,0 +1,16 @@ +const router = require('express').Router(); +const ErrorResponseController = require('./ErrorResponseController'); +const RouteConstant = require('../../constant/Routes'); + +module.exports = (app) => { + router.route('/getAllErrorData') + .get(ErrorResponseController.getAllErrorData); + + router.route('/getErrorDataByCode/:code') + .get(ErrorResponseController.getErrorDataByCode); + + app.use( + RouteConstant.GET_ERROR, + router + ); +}; \ No newline at end of file diff --git a/src/routes/smsRoutes/SmsSendController.js b/src/routes/smsRoutes/SmsSendController.js new file mode 100644 index 0000000..85dc449 --- /dev/null +++ b/src/routes/smsRoutes/SmsSendController.js @@ -0,0 +1,42 @@ +const SmsSendServices = require('../../services/SmsSendServices'); +const reqResponse = require('../../cors/responseHandler'); +const { validationResult } = require('express-validator'); + +module.exports = { + sendSms: async (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(402).send(reqResponse.errorResponse(402)); + } + let data = req.body; + let params = req.params; + let query = req.query; + try { + let result = await SmsSendServices.sendSms(data, params, query); + res.status(201).send(reqResponse.successResponse(201, "SMS Sending Report", result)); + } catch (error) { + console.error(error); + res.status(502).send(reqResponse.errorResponse(502, error)) + } + }, + + verifySms: (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(402).send(reqResponse.errorResponse(402)); + } + let data = req.body; + let params = req.params; + let query = req.query; + SmsSendServices.verifySms(data, params, query) + .then((result) => { + res.status(201).send(reqResponse.successResponse(201, "SMS Status Report", result)); + }) + .catch((error) => { + console.error(error); + res.status(502).send(reqResponse.errorResponse(502, error)); + }) + } +} + + diff --git a/src/routes/smsRoutes/SmsSendRoutes.js b/src/routes/smsRoutes/SmsSendRoutes.js new file mode 100644 index 0000000..d43c4dd --- /dev/null +++ b/src/routes/smsRoutes/SmsSendRoutes.js @@ -0,0 +1,25 @@ +const router = require('express').Router(); +const SmsSendController = require('./SmsSendController'); +const RouteConstant = require('../../constant/Routes'); +const Middleware = require('../../cors/middleware').checkToken; +const Validation = require('../../validation/SmsValidation') + +module.exports = (app) => { + router.route('/send') + .post( + Validation.send(), + SmsSendController.sendSms + ); + + router.route('/status') + .post( + Validation.verify(), + SmsSendController.verifySms + ); + + app.use( + RouteConstant.SMS, + Middleware, + router + ); +}; \ No newline at end of file diff --git a/src/server.js b/src/server.js new file mode 100644 index 0000000..524ad09 --- /dev/null +++ b/src/server.js @@ -0,0 +1,55 @@ +const express = require('express'); +const bodyParser = require('body-parser'); +const chalk = require('chalk'); +const cors = require('cors'); +const app = express(); +const pack = require('../package'); +//const { Pool, Client } = require('pg'); +const path = require('path'); +// if NODE_ENV value not define then dev value will be assign +mode = process.env.NODE_ENV || 'dev'; +// mode can be access anywhere in the project +const config = require('config').get(mode); + +app.use(cors()); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ + extended: false +})); + +// use only when you want to see the metric related to express app +// app.use(require('express-status-monitor')()); + +require('./routes')(app); +const dir = path.join(__dirname, 'assets'); +app.use('/upload', express.static(dir)); + + +const start = () => ( + app.listen(config.port, () => { + console.log(chalk.yellow('.......................................')); + console.log(chalk.green(config.name)); + console.log(chalk.green(`Port:\t\t${config.port}`)); + console.log(chalk.green(`Mode:\t\t${config.mode}`)); + console.log(chalk.green(`App version:\t${pack.version}`)); + console.log(chalk.green("database connection is established")); + console.log(chalk.yellow('.......................................')); + }) +); + +dbConnection = () => { + + /*// PostgreSQL database connection start + const databaseConfig = config.database; + // con can be access anywhere in the project + con = new Pool(databaseConfig); + + app.set('con', con); + //*/ + app.set('config',config); + + start(); + +} + +dbConnection(); \ No newline at end of file diff --git a/src/services/SmsSendServices.js b/src/services/SmsSendServices.js new file mode 100644 index 0000000..c648e1e --- /dev/null +++ b/src/services/SmsSendServices.js @@ -0,0 +1,90 @@ +const https = require('https'); +const config = require('config').get(mode); + +module.exports = { + sendSms: async (data, params, query) => { + return new Promise((resolve, reject) => { + + let urlstr = config.clickatell.base_url + '/message'; + console.log("Clickatell " + urlstr); + + const RestBody = JSON.stringify({ + text: data.text, + to: [ data.number ], + from: config.clickatell.phone_from, + mo: 1 + }); + console.log( RestBody ); + + const options = { + hostname: 'api.clickatell.com', + port: 443, + path: '/rest/message', + method: 'POST', + headers: { + 'X-Version': '1', + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'bearer ' + config.clickatell.api_key, + 'Content-Length': RestBody.length + } + }; + + const req = https.request(options, res => { + console.log(`statusCode: ${res.statusCode}`) + + res.on('data', d => { + process.stdout.write(d); + const responseData = JSON.parse(d.toString('utf8')); + if (responseData.error) { + reject(responseData.error); + } else { + resolve({response: responseData}); + } + }); + }); + + req.on('error', error => { + console.error(error); + reject(error); + }); + + req.write(RestBody); + req.end(); + }); + }, + verifySms: async (data, params, query) => { + return new Promise((resolve, reject) => { + + let urlstr = config.clickatell.base_url + '/message/' + data.apiMessageId; + console.log("Clickatell " + urlstr); + + const options = { + method: 'GET', + headers: { + 'X-Version': '1', + 'Accept': 'application/json', + 'Authorization': 'bearer ' + config.clickatell.api_key + } + }; + + const req = https.request(urlstr, options, (res) => { + console.log('statusCode:', res.statusCode); + console.log('headers:', res.headers); + + res.on('data', (d) => { + process.stdout.write(d); + const responseData = JSON.parse(d.toString('utf8')); + resolve({response: responseData}); + }); + }); + + req.on('error', (error) => { + console.error(error); + reject(error) + }); + req.end(); + }); + }, + +} \ No newline at end of file diff --git a/src/token.js b/src/token.js new file mode 100644 index 0000000..913830c --- /dev/null +++ b/src/token.js @@ -0,0 +1,10 @@ +const jwt = require('jsonwebtoken'); +let token = jwt.sign({ isAdmin: 'true' }, 'secret'); + +// https://www.npmjs.com/package/jsonwebtoken +// iat: Math.floor(Date.now() / 1000) // issued now +// exp: Math.floor(Date.now() / 1000) + (60 * 60) // expires in 1 hr + +console.log(token); + + diff --git a/src/upload/.gitkeep b/src/upload/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/validation/SmsValidation.js b/src/validation/SmsValidation.js new file mode 100644 index 0000000..8a58302 --- /dev/null +++ b/src/validation/SmsValidation.js @@ -0,0 +1,16 @@ +const { body, check } = require('express-validator'); + +module.exports = { + send: () => { + return [ + check("number", "Phone number is required!").not().isEmpty(), + check("text", "SMS text is required!").not().isEmpty(), + ] + }, + + verify: () => { + return [ + check("apiMessageId", "apiMessageId is required!").not().isEmpty(), + ] + } +} \ No newline at end of file diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..f683c52 --- /dev/null +++ b/start.bat @@ -0,0 +1,6 @@ +cd /d %~dp0 +rem "C:\Program Files (x86)\nodejs\npm.cmd" run dev > app.log 2>&1 +:loop +npx cross-env NODE_ENV=dev nodemon --inspect=5001 --config nodemon.js src/server.js +rem 1> app.log 2>&1 +goto loop \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..cb3b13f --- /dev/null +++ b/start.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# +cd /home/chiefsoft/NODES/sms-api +#export PATH="/usr/local/v11.15.0/bin:$PATH" +export NODE_ENV=dev +npm run dev diff --git a/supervisord.conf b/supervisord.conf new file mode 100644 index 0000000..0feac7c --- /dev/null +++ b/supervisord.conf @@ -0,0 +1,16 @@ +[program:sms_api] +command=/home/developer/oameye/sms-api/start.sh +directory=/home/developer/oameye/sms-api +process_name=%(program_name)s +numprocs=1 +umask=022 +priority=1 +user=oameye +group=dev +autostart=true +autorestart=true +redirect_stderr=true +log_stdout=true +log_stderr=true +stdout_logfile=/var/log/supervisor/sms_api.log +