micro-service-maker 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env ADDED
@@ -0,0 +1,7 @@
1
+ NODE_ENV = dev
2
+ DB = mongodb://localhost:27017/vistro
3
+ PORT = 4000
4
+ LAST_PORT = 4000
5
+ CLIENT = http://localhost:3000
6
+ CLIENT_DOMAIN = localhost
7
+ SERVER = http://localhost:4000
package/Dockerfile ADDED
@@ -0,0 +1,15 @@
1
+ FROM node:22-alpine
2
+
3
+ WORKDIR /app
4
+
5
+ COPY package*.json ./
6
+
7
+ RUN npm install
8
+
9
+ COPY . .
10
+
11
+ RUN npm run build
12
+
13
+ EXPOSE 4000
14
+
15
+ CMD ["node", "dist/app.js"]
package/dist/app.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/app.js ADDED
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const prompts_1 = __importDefault(require("prompts"));
9
+ const dotenv_1 = __importDefault(require("dotenv"));
10
+ dotenv_1.default.config();
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const child_process_1 = require("child_process");
14
+ const boilerPlate_1 = require("./utils/boilerPlate");
15
+ const loader_1 = require("./utils/loader");
16
+ /*
17
+ * Check service name entered by user
18
+ * - should not be empty
19
+ * - only letters, numbers and "-" allowed
20
+ */
21
+ const validateService = (service) => {
22
+ if (!service || service.length === 0) {
23
+ throw new Error('Service name is required!');
24
+ }
25
+ const regex = /^[a-zA-Z0-9-]+$/;
26
+ const isValidServiceName = regex.test(service);
27
+ if (!isValidServiceName) {
28
+ throw new Error('Service name must not contain spaces or symbols (only "-" allowed).');
29
+ }
30
+ // convert service name to lowercase
31
+ return service.toLowerCase();
32
+ };
33
+ // *Exit the app with a message
34
+ const exitApp = (message = null) => {
35
+ console.log('\n' + chalk_1.default.red('─'.repeat(40)));
36
+ console.log(`${chalk_1.default.bgRed.white.bold(' EXIT ')} ${chalk_1.default.red(message || 'Process terminated by user.')}`);
37
+ console.log(chalk_1.default.red('─'.repeat(40)) + '\n');
38
+ process.exit(0);
39
+ };
40
+ /*
41
+ * Create a folder
42
+ * Throw error if folder already exists
43
+ */
44
+ const makeFolder = (path) => {
45
+ const isFolderExist = fs_1.default.existsSync(path);
46
+ if (isFolderExist) {
47
+ throw new Error(`${path.split('\\').pop()}: service alredy exists !`);
48
+ }
49
+ fs_1.default.mkdirSync(path);
50
+ };
51
+ // * Copy files from pipeline folder to service folder
52
+ const copyFiles = (files, inputPath, outputPath) => {
53
+ files.forEach((file) => {
54
+ fs_1.default.copyFileSync(path_1.default.join(`${inputPath}`, file), path_1.default.join(`${outputPath}`, file));
55
+ });
56
+ };
57
+ const getBoilerPlateFileData = (file, serviceName) => {
58
+ if (file.endsWith("router.ts")) {
59
+ return (0, boilerPlate_1.routerCode)(serviceName);
60
+ }
61
+ if (file.endsWith("model.ts")) {
62
+ return (0, boilerPlate_1.modelCode)(serviceName);
63
+ }
64
+ if (file.endsWith("interface.ts")) {
65
+ return (0, boilerPlate_1.interfaceCode)(serviceName);
66
+ }
67
+ return "";
68
+ };
69
+ // *Create empty files inside src folder
70
+ const createFiles = (files, serviceName, srcPath) => {
71
+ files.forEach((file) => {
72
+ const fileName = `${serviceName}${file}`;
73
+ const filePath = path_1.default.join(srcPath, fileName);
74
+ fs_1.default.writeFileSync(filePath, getBoilerPlateFileData(file, serviceName));
75
+ });
76
+ };
77
+ // * Increase LAST_PORT value in pipeline .env file
78
+ const updateLastPort = (pipeLinePath) => {
79
+ const envFilePath = path_1.default.join(pipeLinePath, ".env");
80
+ const envData = fs_1.default.readFileSync(envFilePath, "utf-8");
81
+ let lines = envData.split("\n");
82
+ lines = lines.map(line => {
83
+ if (line.trim().startsWith("LAST_PORT")) {
84
+ const [key, value] = line.split("=");
85
+ const newPort = parseInt(value.trim()) + 1;
86
+ return `${key.trim()} = ${newPort}`;
87
+ }
88
+ return line;
89
+ });
90
+ fs_1.default.writeFileSync(envFilePath, lines.join("\n"), "utf-8");
91
+ };
92
+ // * Create .env file for new service
93
+ // * Use LAST_PORT as PORT
94
+ const createEnvForNewService = (pipeLinePath, servicePath) => {
95
+ const envFilePath = path_1.default.join(pipeLinePath, ".env");
96
+ const serviceEnvPath = path_1.default.join(servicePath, ".env");
97
+ const envData = fs_1.default.readFileSync(envFilePath, "utf-8");
98
+ let lines = envData.split("\n");
99
+ let lastPort = null;
100
+ lines = lines.filter(line => {
101
+ if (line.trim().startsWith("LAST_PORT")) {
102
+ const [, value] = line.split("=");
103
+ lastPort = value.trim();
104
+ return false;
105
+ }
106
+ return true;
107
+ });
108
+ if (!lastPort) {
109
+ throw new Error("LAST_PORT not found in pipeline .env file");
110
+ }
111
+ lines = lines.map(line => {
112
+ if (line.trim().startsWith("PORT")) {
113
+ const [key] = line.split("=");
114
+ return `${key.trim()} = ${lastPort}`;
115
+ }
116
+ if (line.trim().startsWith("SERVER")) {
117
+ return line.replace(/:\d+/, `:${lastPort}`);
118
+ }
119
+ return line;
120
+ });
121
+ fs_1.default.writeFileSync(serviceEnvPath, lines.join("\n"), "utf-8");
122
+ };
123
+ const createDockerFileForService = (pipelinePath, servicePath, newPort) => {
124
+ const pipelineDockerFilePath = path_1.default.join(pipelinePath, "Dockerfile");
125
+ const newDockerFilePath = path_1.default.join(servicePath, "Dockerfile");
126
+ const dockerFileData = fs_1.default.readFileSync(pipelineDockerFilePath, "utf-8");
127
+ const replacedWithDocker = dockerFileData.replace(/EXPOSE\s*\d+/, `EXPOSE ${newPort}`);
128
+ fs_1.default.writeFileSync(newDockerFilePath, replacedWithDocker);
129
+ };
130
+ const ceratePackageDotJsonFileForService = (pipeLinePath, servicePath, serviceName) => {
131
+ const sourcePath = 'package.json';
132
+ const destinationPath = path_1.default.join(servicePath, 'package.json');
133
+ let packageJsonRaw = fs_1.default.readFileSync(sourcePath, "utf-8");
134
+ const packageJson = JSON.parse(packageJsonRaw);
135
+ packageJson.name = serviceName;
136
+ packageJson.scripts = {
137
+ ...packageJson.scripts,
138
+ dev: "ts-node-dev --respawn --transpile-only src/app.ts",
139
+ };
140
+ fs_1.default.writeFileSync(destinationPath, JSON.stringify(packageJson, null, 2), "utf-8");
141
+ };
142
+ const getNewPort = (pipeLinePath) => {
143
+ const envPath = path_1.default.join(pipeLinePath, ".env");
144
+ const envData = fs_1.default.readFileSync(envPath, "utf-8");
145
+ const lastPort = envData.split('\n').find(line => line.startsWith("LAST_PORT"))?.split("=")[1]?.trim();
146
+ if (!lastPort) {
147
+ throw new Error("last port not found.");
148
+ }
149
+ return parseInt(lastPort);
150
+ };
151
+ const addGateway = (gatewayPath, serviceName, newPort) => {
152
+ const data = (0, boilerPlate_1.gatewayCode)(serviceName, newPort);
153
+ const input = path_1.default.join(gatewayPath, "src", "app.ts");
154
+ fs_1.default.appendFileSync(input, data);
155
+ };
156
+ const addServer = async (gatewayPath, serviceName) => {
157
+ const mainPath = path_1.default.resolve(gatewayPath, "../../");
158
+ const destinationPath = path_1.default.join(mainPath, "src", "servers.json");
159
+ // 1️Read JSON safely
160
+ const raw = fs_1.default.readFileSync(destinationPath, "utf-8");
161
+ const serversData = JSON.parse(raw);
162
+ // Avoid duplicates
163
+ if (!serversData.includes(serviceName)) {
164
+ serversData.push(serviceName);
165
+ }
166
+ // Write back (pretty format)
167
+ fs_1.default.writeFileSync(destinationPath, JSON.stringify(serversData, null, 2));
168
+ };
169
+ const app = async () => {
170
+ try {
171
+ const msg = " WELCOME TEAM ! ";
172
+ const line = "═".repeat(msg.length);
173
+ console.log(chalk_1.default.blue(`╔${line}╗`));
174
+ console.log(chalk_1.default.blue("║") + chalk_1.default.bgBlue.white.bold(msg) + chalk_1.default.blue("║"));
175
+ console.log(chalk_1.default.blue(`╚${line}╝`));
176
+ // Ask user for service name
177
+ const res = await (0, prompts_1.default)({
178
+ type: 'text',
179
+ name: 'service',
180
+ message: `${chalk_1.default.yellow('⚙')} ${chalk_1.default.white.bold('Service Configuration')}\n ${chalk_1.default.gray('└─')} ${chalk_1.default.blue.bold('Enter service name || To exit app write :q\n')}`,
181
+ format: val => val.trim()
182
+ });
183
+ if (res.service === ":q") {
184
+ exitApp();
185
+ }
186
+ if (res.service === "pipline") {
187
+ exitApp("Pipeline name is not allowed !");
188
+ }
189
+ // validate service name
190
+ const serviceName = validateService(res.service);
191
+ // set all required paths
192
+ const appPath = __dirname;
193
+ const currentDir = process.cwd();
194
+ const pipeLinePath = path_1.default.resolve(appPath, "../");
195
+ const gatewayPath = path_1.default.join(path_1.default.resolve(currentDir, "../"), "gateway");
196
+ const rootPath = currentDir; //path.resolve(appPath, "../../")
197
+ const servicePath = path_1.default.join(rootPath, serviceName);
198
+ const srcPath = path_1.default.join(servicePath, "src");
199
+ const appFilePath = path_1.default.join(srcPath, "app.ts");
200
+ // files to copy from pipeline
201
+ const fileListForCopy = [
202
+ "tsconfig.json",
203
+ ".gitignore",
204
+ "example.env",
205
+ ];
206
+ // files to create in src folder
207
+ const filesListForCreate = [
208
+ ".controller.ts",
209
+ ".service.ts",
210
+ ".interface.ts",
211
+ ".enum.ts",
212
+ ".middleware.ts",
213
+ ".dto.ts",
214
+ ".router.ts",
215
+ ".model.ts"
216
+ ];
217
+ // create folders
218
+ makeFolder(servicePath);
219
+ makeFolder(srcPath);
220
+ // create empty app.ts
221
+ fs_1.default.writeFileSync(appFilePath, (0, boilerPlate_1.appCode)(serviceName));
222
+ // update port and create env file
223
+ updateLastPort(pipeLinePath);
224
+ createEnvForNewService(pipeLinePath, servicePath);
225
+ //crete Packages.json file
226
+ ceratePackageDotJsonFileForService(pipeLinePath, servicePath, serviceName);
227
+ // update port and crete new docker file
228
+ const newPort = getNewPort(pipeLinePath);
229
+ createDockerFileForService(pipeLinePath, servicePath, newPort);
230
+ // copy config files
231
+ copyFiles(fileListForCopy, pipeLinePath, servicePath);
232
+ // create src files
233
+ createFiles(filesListForCreate, serviceName, srcPath);
234
+ // Adding gateway
235
+ addGateway(gatewayPath, serviceName, newPort);
236
+ // Adding services in Server
237
+ await addServer(gatewayPath, serviceName);
238
+ console.log(`\n${chalk_1.default.green('✨')} ${chalk_1.default.white.bold('Generation Complete')}\n` +
239
+ `${chalk_1.default.gray('│')}\n` +
240
+ `${chalk_1.default.gray('└──')} ${chalk_1.default.green.bold('Service:')} ${chalk_1.default.white(serviceName)}\n` +
241
+ `${chalk_1.default.gray('└──')} ${chalk_1.default.green.bold('Status:')} ${chalk_1.default.cyan('Created Successfully')}\n` +
242
+ `${chalk_1.default.gray('└──')} ${chalk_1.default.green.bold('Time:')} ${chalk_1.default.gray(new Date().toLocaleTimeString())}\n`);
243
+ console.log(chalk_1.default.cyan.bold("\n ⚡ SYSTEM UPDATE "));
244
+ console.log(chalk_1.default.black.bgCyan(" PHASE ") + chalk_1.default.cyan(" ❯ Synchronizing Packages..."));
245
+ console.log(chalk_1.default.dim(" ———————————————————————————————————— "));
246
+ (0, loader_1.startLoader)();
247
+ console.log(chalk_1.default.bgWhite.black.bold(" AUTHOR ") +
248
+ chalk_1.default.bgHex('#6272a4').white.bold(" Satyam Sharma ") +
249
+ chalk_1.default.cyan(" ❯❯ ") +
250
+ chalk_1.default.hex('#12c2e9').underline("https://github.com/sharmasatyam121104-devloper"));
251
+ (0, child_process_1.exec)("npm install", { cwd: servicePath }, (err) => {
252
+ if (err && err instanceof Error) {
253
+ throw new Error(`Failed during dependency installation - ${err.message}`);
254
+ }
255
+ console.log("\n");
256
+ const line = "━".repeat(serviceName.length + 50);
257
+ console.log(chalk_1.default.yellow(line));
258
+ console.log(chalk_1.default.yellow(" ✨ ") +
259
+ chalk_1.default.bold.white(serviceName.toUpperCase()) +
260
+ chalk_1.default.green(" dependency install successfully! 🛰️"));
261
+ console.log(chalk_1.default.yellow(line));
262
+ // --- NEXT STEPS SECTION ---
263
+ console.log("\n" + chalk_1.default.cyan.bold(" 👉 NEXT STEPS:"));
264
+ console.log(chalk_1.default.white(" 1. Go back : ") + chalk_1.default.bold.magenta("cd .."));
265
+ console.log(chalk_1.default.white(" 2. Enter Dir: ") + chalk_1.default.bold.magenta(`cd ${serviceName}`));
266
+ console.log(chalk_1.default.white(" 3. Launch : ") + chalk_1.default.bold.bgHex('#FF8C00').black(" npm run dev "));
267
+ exitApp();
268
+ });
269
+ }
270
+ catch (error) {
271
+ if (error instanceof Error) {
272
+ console.log(`\n\n${chalk_1.default.bgRed.white.bold(' ERROR ')} ${chalk_1.default.red(error)}\n`);
273
+ setTimeout(() => {
274
+ app();
275
+ }, 500);
276
+ }
277
+ }
278
+ };
279
+ app();
@@ -0,0 +1,5 @@
1
+ export declare const appCode: (serviceName: string) => string;
2
+ export declare const routerCode: (serviceName: string) => string;
3
+ export declare const interfaceCode: (serviceName: string) => string;
4
+ export declare const modelCode: (serviceName: string) => string;
5
+ export declare const gatewayCode: (serviceName: string, port: number) => string;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gatewayCode = exports.modelCode = exports.interfaceCode = exports.routerCode = exports.appCode = void 0;
4
+ const toPascalCase = (str) => str
5
+ .split("-")
6
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
7
+ .join("");
8
+ const appCode = (serviceName) => `
9
+ import chalk from 'chalk';
10
+ import dotenv from 'dotenv'
11
+ dotenv.config()
12
+
13
+ import mongoose from 'mongoose'
14
+ const DB_URI = process.env.DB;
15
+ if (!DB_URI) {
16
+ throw new Error("DB environment variable is not defined");
17
+ }
18
+
19
+ mongoose.connect(DB_URI)
20
+ .then(() => {
21
+ console.log(
22
+ chalk.bold.green("[ DATABASE ] ") +
23
+ chalk.white("${serviceName.padEnd(12)}") +
24
+ chalk.gray(" | ") +
25
+ chalk.bgGreen.black.bold(" OK ") +
26
+ chalk.green(" Connected Successfully")
27
+ );
28
+ })
29
+ .catch(() => {
30
+ console.log(
31
+ chalk.bold.red("[ DATABASE ] ") +
32
+ chalk.white("${serviceName.padEnd(12)}") +
33
+ chalk.gray(" | ") +
34
+ chalk.bgRed.black.bold(" FAIL ") +
35
+ chalk.red(" Connection Rejected")
36
+ );
37
+ });
38
+
39
+ import express, { Request, Response } from 'express'
40
+ import ${toPascalCase(serviceName)}Router from './${serviceName}.router'
41
+ import morgan from 'morgan'
42
+ import cors from 'cors'
43
+
44
+ const PORT = process.env.PORT || 5000;
45
+ const app = express()
46
+ app.listen(PORT, ()=>{
47
+ console.log(chalk.yellow.bold("${serviceName}" + " - Server running on port http://localhost:" + PORT));
48
+ })
49
+
50
+ app.use(cors({
51
+ origin: process.env.CLIENT,
52
+ credentials: true
53
+ }))
54
+ app.use(express.json())
55
+ app.use(express.urlencoded({ extended: false }))
56
+
57
+ app.use("/${serviceName}", ${toPascalCase(serviceName)}Router)
58
+
59
+ app.get("/", (req: Request, res: Response) => {
60
+ res.send("Hello from ${serviceName} service!");
61
+ });
62
+
63
+ `;
64
+ exports.appCode = appCode;
65
+ const routerCode = (serviceName) => `
66
+ import { Router, Request, Response } from 'express'
67
+
68
+ const ${toPascalCase(serviceName)}Router = Router()
69
+
70
+ ${toPascalCase(serviceName)}Router.get("/", (req: Request, res: Response) => {
71
+ res.json({ message: "Hello from ${serviceName} service" })
72
+ })
73
+
74
+ export default ${toPascalCase(serviceName)}Router
75
+ `;
76
+ exports.routerCode = routerCode;
77
+ const interfaceCode = (serviceName) => `
78
+ import { Document } from "mongoose";
79
+
80
+ export interface ${toPascalCase(serviceName)}Interface extends Document {
81
+
82
+ }
83
+ `;
84
+ exports.interfaceCode = interfaceCode;
85
+ const modelCode = (serviceName) => `
86
+ import { Schema, model } from "mongoose";
87
+ import { ${toPascalCase(serviceName)}Interface } from "./${serviceName}.interface";
88
+
89
+ const ${toPascalCase(serviceName).charAt(0).toLowerCase() + toPascalCase(serviceName).slice(1)}Schema = new Schema<${toPascalCase(serviceName)}Interface>(
90
+ {
91
+
92
+ },
93
+ { timestamps: true }
94
+ );
95
+
96
+ const ${toPascalCase(serviceName)}Model = model<${toPascalCase(serviceName)}Interface>("${toPascalCase(serviceName)}",${toPascalCase(serviceName).charAt(0).toLowerCase() + toPascalCase(serviceName).slice(1)}Schema);
97
+
98
+ export default ${toPascalCase(serviceName)}Model;
99
+ `;
100
+ exports.modelCode = modelCode;
101
+ const gatewayCode = (serviceName, port) => `
102
+ app.use('/${serviceName}', createProxyMiddleware({
103
+ target: 'http://localhost:${port}',
104
+ changeOrigin: true
105
+ }));
106
+ `;
107
+ exports.gatewayCode = gatewayCode;
@@ -0,0 +1 @@
1
+ export declare const startLoader: () => NodeJS.Timeout;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.startLoader = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const startLoader = () => {
9
+ const P = ["\\", "|", "/", "-"];
10
+ let x = 0;
11
+ return setInterval(() => {
12
+ process.stdout.write(`\r${chalk_1.default.cyan(P[x++ % P.length])} ${chalk_1.default.white("Installing dependencies... ")}`);
13
+ }, 100);
14
+ };
15
+ exports.startLoader = startLoader;
package/example.env ADDED
File without changes
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "micro-service-maker",
3
+ "version": "1.0.0",
4
+ "main": "dist/app.js",
5
+ "types": "dist/app.d.ts",
6
+ "bin": {
7
+ "msm": "dist/app.js"
8
+ },
9
+ "scripts": {
10
+ "dev": "ts-node-dev --transpile-only src/app.ts",
11
+ "build": "tsc",
12
+ "start": "node dist/app.js"
13
+ },
14
+ "keywords": [],
15
+ "author": "",
16
+ "license": "ISC",
17
+ "description": "",
18
+ "dependencies": {
19
+ "chalk": "^4.1.2",
20
+ "cors": "^2.8.6",
21
+ "dotenv": "^17.2.3",
22
+ "express": "^5.2.1",
23
+ "mongoose": "^9.1.5",
24
+ "morgan": "^1.10.1",
25
+ "prompts": "^2.4.2"
26
+ },
27
+ "devDependencies": {
28
+ "@types/cors": "^2.8.19",
29
+ "@types/express": "^5.0.6",
30
+ "@types/morgan": "^1.9.10",
31
+ "@types/node": "^25.2.0",
32
+ "@types/prompts": "^2.4.9",
33
+ "ts-node-dev": "^2.0.0",
34
+ "typescipt": "^1.0.0"
35
+ }
36
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "node16",
5
+ "outDir": "./dist",
6
+ "rootDir": "./src",
7
+ "strict": true,
8
+ "declaration": true,
9
+ "esModuleInterop": true,
10
+ "moduleResolution": "node16",
11
+ "forceConsistentCasingInFileNames": true,
12
+ "skipLibCheck": true
13
+ },
14
+ "include": ["src/**/*"],
15
+ "exclude": ["dist"]
16
+ }