diff --git a/.gitignore b/.gitignore index 28f2292..28f163c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ # Node Modules -/node_modules/ \ No newline at end of file +/node_modules/ +test/ \ No newline at end of file diff --git a/bin/configs.js b/bin/configs.js index 6055076..f450b3f 100644 --- a/bin/configs.js +++ b/bin/configs.js @@ -66,4 +66,29 @@ export const templates = { }, ], }, + express_mysql: { + name: "express_mysql", + dependencies: [ + { + name: "express", + version: "^4.17.1", + }, + { + name: "cors", + version: "^2.8.5", + }, + { + name: "fs", + version: "^0.0.1-security", + }, + { + name: "helmet", + version: "^8.0.0", + }, + { + name: "mysql2", + version: "^3.11.3", + }, + ], + }, }; diff --git a/templates/express_mysql/config/appConfig.js b/templates/express_mysql/config/appConfig.js new file mode 100644 index 0000000..d3e7760 --- /dev/null +++ b/templates/express_mysql/config/appConfig.js @@ -0,0 +1,23 @@ +const CONCURRENCY_LIMIT = 4; +export const appConfig = { + PORT: 5000, + db: { + host: 'localhost', + user: 'root', + password: 'password', + database: 'database_name', + multipleStatements: true + }, + pool_db: { + host: 'localhost', + user: 'root', + password: 'password', + database: 'database_name', + waitForConnections: true, + connectionLimit: CONCURRENCY_LIMIT, + queueLimit: 0, + }, + router: { + SAMPLE_PREFIX: '/api/sample', + }, +} \ No newline at end of file diff --git a/templates/express_mysql/connection/normalConnection.js b/templates/express_mysql/connection/normalConnection.js new file mode 100644 index 0000000..b4637c8 --- /dev/null +++ b/templates/express_mysql/connection/normalConnection.js @@ -0,0 +1,19 @@ +import { appConfig } from '../config/appConfig.js' +import { appendFileSync } from 'fs' +import { createConnection } from 'mysql2' + +const connectToDb = () => { + let db = null + try { + db = createConnection(appConfig.db) + appendFileSync('./logs/connection/normalConnection.log', 'asdada'); + return db + } catch (err) { + const timeStamp = new Date().toLocaleString(); + const errMessage = `[ERROR]: ${timeStamp} - ${err.message}` + console.error(errMessage); + appendFileSync('./logs/connection/normalConnection.log', `${errMessage}\n`); + } +} + +export default connectToDb \ No newline at end of file diff --git a/templates/express_mysql/connection/poolConnection.js b/templates/express_mysql/connection/poolConnection.js new file mode 100644 index 0000000..7750a6b --- /dev/null +++ b/templates/express_mysql/connection/poolConnection.js @@ -0,0 +1,15 @@ +import { createPool } from 'mysql2' +import { appConfig } from '../config/appConfig.js' +import { appendFileSync } from 'fs' + +let db = null +try { + db = createPool(appConfig.pool_db) +} catch (err) { + const timeStamp = new Date().toLocaleString(); + const errMessage = `[ERROR]: ${timeStamp} - ${err.message}` + console.error(errMessage); + appendFileSync('./logs/connection/poolConnection.log', `${errMessage}\n`); +} + +export default db \ No newline at end of file diff --git a/templates/express_mysql/controller/sampleController.js b/templates/express_mysql/controller/sampleController.js new file mode 100644 index 0000000..140712d --- /dev/null +++ b/templates/express_mysql/controller/sampleController.js @@ -0,0 +1,32 @@ +import { appendFileSync } from 'fs'; +import db from '../connection/poolConnection.js'; + +export async function test(_, res) { + return res.status(200).send({ + "MESSAGE": "It's Working. 👍🏻", + }); +} + +export async function getAllSamples(_, res) { + const db_conn = await db.promise().getConnection(); + try { + await db_conn.query('LOCK TABLES sample_table READ'); + const [data] = await db_conn.query('SELECT * FROM sample_table'); + return res.status(200).send({ + "MESSAGE": "Data fetched successfully.", + "DATA": data + }); + } catch { + const timeStamp = new Date().toLocaleString(); + const errMessage = `[ERROR]: ${timeStamp} - ${err.message}`; + console.error(errMessage); + appendFileSync('./logs/controller/sampleController.log', `${errMessage}\n`); + + return res.status(500).send({ + "MESSAGE": "Something went wrong. Please try again later.", + }); + } finally { + await db_conn.query('UNLOCK TABLES'); + db_conn.release(); + } +} diff --git a/templates/express_mysql/db/reInitDb.js b/templates/express_mysql/db/reInitDb.js new file mode 100644 index 0000000..d6d6039 --- /dev/null +++ b/templates/express_mysql/db/reInitDb.js @@ -0,0 +1,36 @@ +import { readFile, appendFileSync } from 'fs'; +import { appConfig } from '../config/appConfig.js'; + +const reInitDb = (db) => { + try { + readFile('./db/reInitDb.sql', 'utf8', (err, data) => { + if (err) { + const timeStamp = new Date().toLocaleString(); + const errMessage = `[ERROR]: ${timeStamp} - reInitDb - ${err.message}` + console.error(errMessage); + } else { + db.query(data, (err, _) => { + if (err) { + const timeStamp = new Date().toLocaleString(); + const errMessage = `[ERROR]: ${timeStamp} - reInitDb - ${err.message}` + console.error(errMessage); + + if (err.message.includes('Unknown database')) { + console.warn(`[HINT]: Database '${appConfig.db.database}' does not exist. Please create it by running the following command in your MySQL shell: 'CREATE DATABASE ${appConfig.db.database}'.`); + } + + } else { + console.info('[INFO]: Database re-initialized.'); + } + }); + } + }); + } catch (err) { + const timeStamp = new Date().toLocaleString(); + const errMessage = `[ERROR]: ${timeStamp} - ${err.message}` + console.error(errMessage); + appendFileSync('./logs/db/reInitDb.log', `${errMessage}\n`); + } +} + +export default reInitDb; \ No newline at end of file diff --git a/templates/express_mysql/db/reInitDb.sql b/templates/express_mysql/db/reInitDb.sql new file mode 100644 index 0000000..70f03c1 --- /dev/null +++ b/templates/express_mysql/db/reInitDb.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS `sample_table`; +CREATE TABLE `sample_table` ( + `id` INT NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) DEFAULT NULL, + PRIMARY KEY (`id`) +); +INSERT INTO `sample_table` (`name`) +VALUES ('sample1'); +INSERT INTO `sample_table` (`name`) +VALUES ('sample2'); \ No newline at end of file diff --git a/templates/express_mysql/index.js b/templates/express_mysql/index.js new file mode 100644 index 0000000..3cff5e8 --- /dev/null +++ b/templates/express_mysql/index.js @@ -0,0 +1,40 @@ +import express, { json } from 'express' +import helmet from 'helmet' +import { appendFileSync } from 'fs' +import cors from 'cors' + +import { initLog } from './logs/initLog.js' +import sampleRouter from './router/sampleRouter.js' +import { appConfig } from './config/appConfig.js' +import connectToDb from './connection/normalConnection.js' +import reInitDb from './db/reInitDb.js' + +const app = express() + +// Helmet sets HTTP headers for security. +app.use(helmet()) + +app.use(cors()) +app.use(json()) + +// Disable the X-Powered-By header to make it harder +// for attackers to find the tech stack. +app.disable('x-powered-by') + +app.use(appConfig.router.SAMPLE_PREFIX, sampleRouter) + +initLog() +const db = connectToDb() +reInitDb(db) + +app.listen(appConfig.PORT, (err) => { + if (err) { + const timeStamp = new Date().toLocaleString(); + const errMessage = `[ERROR]: ${timeStamp} - ${err.message}` + console.error(errMessage); + appendFileSync('./logs/index.log', `${errMessage}\n`); + } else { + console.info(`[INFO]: Server is running on http://127.0.0.1:${appConfig.PORT}.`); + console.warn(`[TEST]: Test the server by sending a GET request to http://127.0.0.1:${appConfig.PORT}${appConfig.router.SAMPLE_PREFIX}/test.`); + } +}) diff --git a/templates/express_mysql/logs/initLog.js b/templates/express_mysql/logs/initLog.js new file mode 100644 index 0000000..0e8fc2c --- /dev/null +++ b/templates/express_mysql/logs/initLog.js @@ -0,0 +1,19 @@ +import { existsSync, mkdirSync } from 'fs'; + +export const initLog = () => { + if (!existsSync('./logs')) { + mkdirSync('./logs'); + } + + if (!existsSync('./logs/controller')) { + mkdirSync('./logs/controller'); + } + + if (!existsSync('./logs/db')) { + mkdirSync('./logs/db'); + } + + if (!existsSync('./logs/connection')) { + mkdirSync('./logs/connection'); + } +} \ No newline at end of file diff --git a/templates/express_mysql/package.json b/templates/express_mysql/package.json new file mode 100644 index 0000000..d1b9f3e --- /dev/null +++ b/templates/express_mysql/package.json @@ -0,0 +1,19 @@ +{ + "name": "express_mysql", + "version": "1.0.0", + "description": "Backend template for Express.js server with MySQL db.", + "main": "index.js", + "scripts": { + "start": "node index.js" + }, + "author": "quick_start_express", + "license": "ISC", + "dependencies": { + "cors": "^2.8.5", + "express": "^4.21.1", + "fs": "^0.0.1-security", + "helmet": "^8.0.0", + "mysql2": "^3.11.3" + }, + "type": "module" +} diff --git a/templates/express_mysql/router/sampleRouter.js b/templates/express_mysql/router/sampleRouter.js new file mode 100644 index 0000000..8fc67b4 --- /dev/null +++ b/templates/express_mysql/router/sampleRouter.js @@ -0,0 +1,9 @@ +import { test, getAllSamples } from '../controller/sampleController.js' +import { Router } from 'express' + +const sampleRouter = Router() + +sampleRouter.get('/test', test) +sampleRouter.get('/all', getAllSamples) + +export default sampleRouter \ No newline at end of file