micflow 0.0.0-stage → 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,8 @@
1
+ NODE_ENV=dev
2
+ PORT=4000
3
+ LAST_PORT = 4001
4
+ DB=mongodb://localhost:27017/zivio
5
+ CLIENT=http://localhost:3000
6
+ DOMAIN=localhost
7
+ SERVER=http://localhost:4000
8
+
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,226 @@
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 dotenv_1 = __importDefault(require("dotenv"));
8
+ dotenv_1.default.config();
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const inquirer_1 = __importDefault(require("inquirer"));
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const child_process_1 = require("child_process");
14
+ const util_1 = require("util");
15
+ const boilerplate_1 = require("./utils/boilerplate");
16
+ const log = console.log;
17
+ const Exec = (0, util_1.promisify)(child_process_1.exec);
18
+ // console.log("dirname", __dirname)
19
+ // console.log(process.cwd())
20
+ const validateService = (service) => {
21
+ if (!service || service.length === 0) {
22
+ throw new Error(chalk_1.default.bgRed.white.bold("Service is required"));
23
+ }
24
+ const reqex = /^[a-zA-Z0-9-]+$/;
25
+ const isValid = reqex.test(service);
26
+ if (!isValid) {
27
+ throw new Error(chalk_1.default.bgRed.white.bold("Space or any symbol not allowed in service name so you can use -"));
28
+ }
29
+ return service.toLowerCase();
30
+ };
31
+ const exitApp = (msg = null) => {
32
+ const message = chalk_1.default.bgGreenBright.black.bold(msg || "👋 Good Bye !");
33
+ log(message);
34
+ process.exit(0);
35
+ };
36
+ const makeFolder = async (path) => {
37
+ const isExists = fs_1.default.existsSync(path);
38
+ if (isExists) {
39
+ throw new Error(`${path.split("\\").pop()} service is already exists !`);
40
+ }
41
+ fs_1.default.mkdirSync(path);
42
+ };
43
+ const copyFiles = (files, inputPath, outputPath) => {
44
+ files.forEach((file) => {
45
+ // src, destination
46
+ fs_1.default.copyFileSync(path_1.default.join(`${inputPath}`, file), path_1.default.join(`${outputPath}`, file));
47
+ });
48
+ };
49
+ const getBiolerplate = (file, service) => {
50
+ if (file === ".router.ts")
51
+ return (0, boilerplate_1.routerBoilerplate)(service);
52
+ if (file === ".interface.ts")
53
+ return (0, boilerplate_1.interfaceBoilerplate)(service);
54
+ if (file === ".model.ts")
55
+ return (0, boilerplate_1.modelBoilerplate)(service);
56
+ return "";
57
+ };
58
+ const createFiles = (files, service, srcPath) => {
59
+ files.forEach((file) => {
60
+ const filename = `${service}${file}`;
61
+ const filepath = path_1.default.join(srcPath, filename);
62
+ fs_1.default.writeFileSync(filepath, getBiolerplate(file, service));
63
+ });
64
+ };
65
+ const updateLastPort = (pipelinePath, newPort) => {
66
+ const envFilePath = path_1.default.join(pipelinePath, ".env");
67
+ const envData = fs_1.default.readFileSync(envFilePath, "utf-8");
68
+ const updatedPortString = envData.replace(/LAST_PORT\s*=\s*\d+/, `LAST_PORT = ${newPort}`);
69
+ fs_1.default.writeFileSync(envFilePath, updatedPortString);
70
+ };
71
+ const createEnvForNewService = (pipelinePath, servicePath, newPort) => {
72
+ const pipelineEnvPath = path_1.default.join(pipelinePath, ".env");
73
+ const newEnvPath = path_1.default.join(servicePath, ".env");
74
+ const envData = fs_1.default.readFileSync(pipelineEnvPath, "utf-8");
75
+ const stringChangedAfterPort = envData.replace(/PORT\s*=\s*\d+/, `PORT = ${newPort}`);
76
+ const arr = stringChangedAfterPort.split("\n");
77
+ const modifiedData = arr.map((item) => {
78
+ if (item.startsWith("LAST_PORT"))
79
+ return null;
80
+ if (item.startsWith("SERVER"))
81
+ return `SERVER = http://localhost:${newPort}\r`;
82
+ return item;
83
+ }).filter(Boolean);
84
+ const finalData = modifiedData.join("\n");
85
+ fs_1.default.writeFileSync(newEnvPath, finalData);
86
+ };
87
+ const createPackageForNewService = (service, servicePath) => {
88
+ const data = (0, boilerplate_1.packageBoilerplate)(service);
89
+ const newPackagePath = path_1.default.join(servicePath, "package.json");
90
+ fs_1.default.writeFileSync(newPackagePath, data);
91
+ };
92
+ const createDockerFileForNewService = (pipelinePath, servicePath, newPort) => {
93
+ // console.log("New port", newPort)
94
+ const pipelineDockerFilePath = path_1.default.join(pipelinePath, "Dockerfile");
95
+ const newDockerFilePath = path_1.default.join(servicePath, "Dockerfile");
96
+ const dockerFileData = fs_1.default.readFileSync(pipelineDockerFilePath, "utf-8");
97
+ // console.log("Docker file data", dockerFileData)
98
+ const replacedWithDocker = dockerFileData.replace(/EXPOSE\s*\d+/, `EXPOSE ${newPort}`);
99
+ // console.log("Replacew with docker", replacedWithDocker)
100
+ fs_1.default.writeFileSync(newDockerFilePath, replacedWithDocker);
101
+ // console.log(
102
+ // "Saved Dockerfile:",
103
+ // fs.readFileSync(newDockerFilePath, "utf-8")
104
+ // );
105
+ };
106
+ const getNewPort = (pipelinePath) => {
107
+ const envPath = path_1.default.join(pipelinePath, ".env");
108
+ const envData = fs_1.default.readFileSync(envPath, "utf-8");
109
+ const arr = envData.split("\n");
110
+ const lastPortLine = arr.find((line) => {
111
+ return line.trimStart().startsWith("LAST_PORT");
112
+ });
113
+ // console.log("lastport line", lastPortLine)
114
+ const regExpForPortValue = /LAST_PORT\s*=\s*(\d+)/;
115
+ const lastPort = parseInt(lastPortLine?.match(regExpForPortValue)?.[1]);
116
+ return lastPort + 1;
117
+ };
118
+ const addGateway = (gatewayPath, serviceName, newPort) => {
119
+ const data = (0, boilerplate_1.gatewayBoilerplate)(serviceName, newPort);
120
+ const input = path_1.default.join(gatewayPath, "src", "app.ts");
121
+ fs_1.default.appendFileSync(input, data);
122
+ };
123
+ const addServer = async (gatewayPath, serviceName) => {
124
+ const main = path_1.default.resolve(gatewayPath, "../../");
125
+ const inputPath = path_1.default.join(main, "src", "servers.json");
126
+ try {
127
+ const fileContent = fs_1.default.readFileSync(inputPath, "utf-8");
128
+ const servers = JSON.parse(fileContent);
129
+ if (!servers.includes(serviceName)) {
130
+ servers.push(serviceName);
131
+ fs_1.default.writeFileSync(inputPath, JSON.stringify(servers, null, 2), "utf-8");
132
+ }
133
+ // console.log("Servers:", servers);
134
+ }
135
+ catch (error) {
136
+ console.error("Failed to update servers.json:", error);
137
+ throw error;
138
+ }
139
+ };
140
+ const app = async () => {
141
+ try {
142
+ const welcomeMessage = chalk_1.default.bgGreenBright("======= Weclome Team ! =========\n");
143
+ log(welcomeMessage);
144
+ const { service } = await inquirer_1.default.prompt({
145
+ type: "input",
146
+ name: "service",
147
+ message: chalk_1.default.yellow("Enter serivce name || to exit press :q\n")
148
+ });
149
+ if (service === ":q") {
150
+ exitApp();
151
+ }
152
+ if (service === "pipeline") {
153
+ exitApp("Pipeline name not allowed");
154
+ }
155
+ const serviceName = validateService(service);
156
+ // console.log(serviceName)
157
+ // Get the current working directory
158
+ const currentDir = process.cwd();
159
+ // Get the current module directory
160
+ const appPath = __dirname;
161
+ // Set the project root directory
162
+ const rootPath = currentDir;
163
+ // Resolve the pipeline directory
164
+ const pipelinePath = path_1.default.resolve(appPath, "../");
165
+ // Resolve the API gateway directory
166
+ const gatewayPath = path_1.default.join(path_1.default.resolve(currentDir, "../"), "gateway");
167
+ // Define the new service directory
168
+ const servicePath = path_1.default.join(rootPath, serviceName);
169
+ // Define the service source directory
170
+ const srcPath = path_1.default.join(servicePath, "src");
171
+ // Define the service entry point file
172
+ const appFilePath = path_1.default.join(srcPath, "app.ts");
173
+ const fileListForCopy = [
174
+ "package-lock.json",
175
+ "tsconfig.json"
176
+ ];
177
+ const filesListForCreate = [
178
+ ".controller.ts",
179
+ ".service.ts",
180
+ ".model.ts",
181
+ ".interface.ts",
182
+ ".enum.ts",
183
+ ".middleware.ts",
184
+ ".dto.ts",
185
+ ".router.ts"
186
+ ];
187
+ // Service folder
188
+ makeFolder(servicePath);
189
+ // Src folder inside service
190
+ makeFolder(srcPath);
191
+ // Creating app.ts
192
+ const newPort = getNewPort(pipelinePath);
193
+ // console.log("new ports", newPort)
194
+ const content = (0, boilerplate_1.appBoilerplate)(serviceName, newPort);
195
+ // console.log("App boilerplate", content)
196
+ fs_1.default.writeFileSync(appFilePath, content, "utf-8");
197
+ // Change last port in pipeline
198
+ updateLastPort(pipelinePath, newPort);
199
+ createEnvForNewService(pipelinePath, servicePath, newPort);
200
+ createDockerFileForNewService(pipelinePath, servicePath, newPort);
201
+ createPackageForNewService(serviceName, servicePath);
202
+ // Copying all required file or typescript
203
+ copyFiles(fileListForCopy, pipelinePath, servicePath);
204
+ // Creating required files for start coding
205
+ createFiles(filesListForCreate, serviceName, srcPath);
206
+ // Adding Gateway
207
+ addGateway(gatewayPath, serviceName, newPort);
208
+ // Adding server
209
+ await addServer(gatewayPath, serviceName);
210
+ log(chalk_1.default.bgGreen.black.bold(" 🚀 Installing dependencies... Please wait ⏳ "));
211
+ await Exec("npm install", { cwd: servicePath });
212
+ // await Exec("npm run dev", { cwd: servicePath })
213
+ log(chalk_1.default.bgYellow.black.bold(`Success - ${serviceName} created successfully !`));
214
+ log(chalk_1.default.green.bold(`🚀Browse service folder and write npm run dev`));
215
+ exitApp();
216
+ // log(chalk.bgYellowBright.black.bold(`Success - ${serviceName} created successfully !`))
217
+ // exitApp()
218
+ }
219
+ catch (error) {
220
+ if (error instanceof Error) {
221
+ log(chalk_1.default.bgRed.white.bold(` 🛑 Error - ${error.message} \n`));
222
+ app();
223
+ }
224
+ }
225
+ };
226
+ app();
@@ -0,0 +1,6 @@
1
+ export declare const appBoilerplate: (service: string, port: number) => string;
2
+ export declare const routerBoilerplate: (service: string) => string;
3
+ export declare const interfaceBoilerplate: (service: string) => string;
4
+ export declare const modelBoilerplate: (service: string) => string;
5
+ export declare const packageBoilerplate: (service: string) => string;
6
+ export declare const gatewayBoilerplate: (service: string, port: number) => string;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gatewayBoilerplate = exports.packageBoilerplate = exports.modelBoilerplate = exports.interfaceBoilerplate = exports.routerBoilerplate = exports.appBoilerplate = void 0;
4
+ const getServiceInPascalCase = (service) => {
5
+ const arr = service.split("-");
6
+ const tmp = arr.map((item) => {
7
+ const firstLetter = item[0].toUpperCase();
8
+ const restLetter = item.slice(1);
9
+ return firstLetter + restLetter;
10
+ });
11
+ return tmp.join("");
12
+ };
13
+ const appBoilerplate = (service, port) => {
14
+ return [
15
+ `import dotenv from "dotenv"`,
16
+ `dotenv.config()\n`,
17
+ `import mongoose from "mongoose"`,
18
+ `mongoose.connect(process.env.DB!)`,
19
+ `.then(()=>console.log("${service} - Database is running"))`,
20
+ `.catch(()=>console.log("${service} - Failed to connect with database"))\n`,
21
+ `import express, { Request, Response } from "express"`,
22
+ `import ${getServiceInPascalCase(service)}Router from "./${service}.router"`,
23
+ `import morgan from "morgan"`,
24
+ `import cors from "cors"`,
25
+ `const app = express()`,
26
+ `app.listen(process.env.PORT, ()=>console.log("${service} service is running on - http://localhost:${port}/${service}"))\n`,
27
+ `app.use(cors({`,
28
+ `\torigin: process.env.CLIENT,`,
29
+ `\tcredentials: true`,
30
+ `}))`,
31
+ `app.use(express.json())`,
32
+ `app.use(express.urlencoded({extended: false}))\n`,
33
+ `app.use("/${service}", ${getServiceInPascalCase(service)}Router)`
34
+ ].join("\n");
35
+ };
36
+ exports.appBoilerplate = appBoilerplate;
37
+ const routerBoilerplate = (service) => {
38
+ return [
39
+ `import {Router, Request, Response} from "express"`,
40
+ `const ${getServiceInPascalCase(service)}Router = Router()\n`,
41
+ `${getServiceInPascalCase(service)}Router.get("/", (req: Request, res: Response)=>{`,
42
+ `\tres.json({message: "Hello from ${service} service"})`,
43
+ `})\n`,
44
+ `export default ${getServiceInPascalCase(service)}Router`
45
+ ].join("\n");
46
+ };
47
+ exports.routerBoilerplate = routerBoilerplate;
48
+ const interfaceBoilerplate = (service) => {
49
+ const Provider = getServiceInPascalCase(service);
50
+ return [
51
+ `import { Document } from "mongoose"\n`,
52
+ `export interface ${Provider}ModelInterface extends Document {\n`,
53
+ `\t`,
54
+ `}`
55
+ ].join("\n");
56
+ };
57
+ exports.interfaceBoilerplate = interfaceBoilerplate;
58
+ const modelBoilerplate = (service) => {
59
+ const Provider = getServiceInPascalCase(service);
60
+ return [
61
+ `import { Schema, model } from "mongoose"`,
62
+ `import { ${Provider}ModelInterface } from "./${service}.interface"\n`,
63
+ `const schema = new Schema<${Provider}ModelInterface>({\n`,
64
+ `\t`,
65
+ `}, {timestamps: true})\n`,
66
+ `const ${Provider}Model = model<${Provider}ModelInterface>("${Provider}", schema)`,
67
+ `export default ${Provider}Model`
68
+ ].join("\n");
69
+ };
70
+ exports.modelBoilerplate = modelBoilerplate;
71
+ const packageBoilerplate = (service) => {
72
+ const data = {
73
+ "name": service,
74
+ "version": "1.0.0",
75
+ "description": "",
76
+ "main": "index.js",
77
+ "scripts": {
78
+ "dev": "ts-node-dev --respawn --transpile-only src/app.ts",
79
+ "build": "tsc",
80
+ "start": "node dist/app.js"
81
+ },
82
+ "keywords": [],
83
+ "author": "",
84
+ "license": "ISC",
85
+ "type": "commonjs",
86
+ "dependencies": {
87
+ "chalk": "^6.0.1",
88
+ "cors": "^2.8.6",
89
+ "dotenv": "^18.0.8",
90
+ "express": "^5.3.0",
91
+ "inquirer": "^14.2.4",
92
+ "mongoose": "^9.11.2",
93
+ "morgan": "^1.12.1"
94
+ },
95
+ "devDependencies": {
96
+ "@types/cors": "^2.8.19",
97
+ "@types/express": "^5.0.6",
98
+ "@types/morgan": "^1.9.10",
99
+ "@types/node": "^26.6.5",
100
+ "ts-node-dev": "^2.0.0",
101
+ "typescript": "^6.0.3"
102
+ }
103
+ };
104
+ return JSON.stringify(data, null, 2);
105
+ };
106
+ exports.packageBoilerplate = packageBoilerplate;
107
+ const gatewayBoilerplate = (service, port) => {
108
+ return [
109
+ `\n\napp.use("/${service}", createProxyMiddleware({`,
110
+ `\ttarget: 'http://localhost:${port}/${service}',`,
111
+ `\tchangeOrigin: true`,
112
+ `}))`
113
+ ].join("\n");
114
+ };
115
+ exports.gatewayBoilerplate = gatewayBoilerplate;
package/package.json CHANGED
@@ -1,6 +1,38 @@
1
1
  {
2
2
  "name": "micflow",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "A CLI tool to scaffold microservices and configure gateway routing",
5
+ "main": "dist/app.js",
6
+ "types": "dist/app.d.ts",
7
+ "bin": {
8
+ "micflow": "dist/app.js"
9
+ },
10
+ "scripts": {
11
+ "dev": "ts-node-dev --transpile-only src/app.ts",
12
+ "build": "tsc",
13
+ "start": "node dist/app.js"
14
+ },
15
+ "keywords": [],
16
+ "author": "",
17
+ "license": "ISC",
18
+ "type": "commonjs",
19
+ "dependencies": {
20
+ "chalk": "^4.1.2",
21
+ "cors": "^2.8.6",
22
+ "dotenv": "^18.0.8",
23
+ "express": "^5.3.0",
24
+ "inquirer": "^8.2.6",
25
+ "install": "^0.13.0",
26
+ "mongoose": "^9.11.2",
27
+ "morgan": "^1.12.1"
28
+ },
29
+ "devDependencies": {
30
+ "@types/cors": "^2.8.19",
31
+ "@types/express": "^5.0.6",
32
+ "@types/inquirer": "^8.2.13",
33
+ "@types/morgan": "^1.9.10",
34
+ "@types/node": "^26.6.5",
35
+ "ts-node-dev": "^2.0.0",
36
+ "typescript": "^6.0.3"
37
+ }
38
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "Node16",
5
+ "moduleResolution": "node16",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "declaration": true,
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "skipLibCheck": true
13
+ },
14
+ "include": ["src/**/*"],
15
+ "exclude": ["dist"]
16
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.