go-gin-cli 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/README.md +287 -0
- package/git-manager.sh +495 -0
- package/package.json +36 -0
- package/prompt.md +6 -0
- package/src/bin/index.js +410 -0
- package/src/lib/constants/index.js +24 -0
- package/src/lib/shares/createDir.js +22 -0
- package/src/lib/shares/createFile.js +23 -0
- package/src/lib/utils/add-auth-to-resource.js +225 -0
- package/src/lib/utils/create-auth.js +937 -0
- package/src/lib/utils/create-resource.js +1426 -0
- package/src/lib/utils/create-service.js +456 -0
- package/src/lib/utils/display.js +19 -0
- package/src/lib/utils/help.js +93 -0
- package/src/lib/utils/remove-module.js +146 -0
- package/src/lib/utils/setup.js +1626 -0
package/src/bin/index.js
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const prompts = require("prompts");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
|
|
7
|
+
const { displayVersion } = require("../lib/utils/display");
|
|
8
|
+
|
|
9
|
+
// Colors for output
|
|
10
|
+
const COLORS = {
|
|
11
|
+
GREEN: "\x1b[32m",
|
|
12
|
+
YELLOW: "\x1b[33m",
|
|
13
|
+
RED: "\x1b[31m",
|
|
14
|
+
CYAN: "\x1b[36m",
|
|
15
|
+
MAGENTA: "\x1b[35m",
|
|
16
|
+
NC: "\x1b[0m",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Command-line arguments
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
|
|
22
|
+
// Script paths
|
|
23
|
+
const scriptDir = path.join(__dirname);
|
|
24
|
+
const scripts = {
|
|
25
|
+
help: path.join(scriptDir, "./../lib/utils/help.js"),
|
|
26
|
+
createResource: path.join(scriptDir, "./../lib/utils/create-resource.js"),
|
|
27
|
+
createService: path.join(scriptDir, "./../lib/utils/create-service.js"),
|
|
28
|
+
createAuth: path.join(scriptDir, "./../lib/utils/create-auth.js"),
|
|
29
|
+
addAuthToResource: path.join(
|
|
30
|
+
scriptDir,
|
|
31
|
+
"./../lib/utils/add-auth-to-resource.js",
|
|
32
|
+
),
|
|
33
|
+
removeModule: path.join(scriptDir, "./../lib/utils/remove-module.js"),
|
|
34
|
+
setup: path.join(scriptDir, "./../lib/utils/setup.js"),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Utility functions
|
|
38
|
+
const utils = {
|
|
39
|
+
validateName: (name) => {
|
|
40
|
+
const isValid = /^[a-z][a-z-]*$/.test(name);
|
|
41
|
+
if (!isValid) {
|
|
42
|
+
console.error(
|
|
43
|
+
`${COLORS.RED}❌ Invalid name format: Use lowercase with hyphens (e.g., user-profile)${COLORS.NC}`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return isValid;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
processName: (name) => ({
|
|
50
|
+
lower: name,
|
|
51
|
+
pascal: name
|
|
52
|
+
.split("-")
|
|
53
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
54
|
+
.join(""),
|
|
55
|
+
camel: name
|
|
56
|
+
.split("-")
|
|
57
|
+
.map((part, i) => i === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1))
|
|
58
|
+
.join(""),
|
|
59
|
+
snake: name.replace(/-/g, "_"),
|
|
60
|
+
}),
|
|
61
|
+
|
|
62
|
+
showStructure: () => {
|
|
63
|
+
try {
|
|
64
|
+
console.log(`${COLORS.GREEN}✅ Operation completed!${COLORS.NC}`);
|
|
65
|
+
console.log(`${COLORS.YELLOW}📂 Project structure:${COLORS.NC}`);
|
|
66
|
+
const cmd =
|
|
67
|
+
process.platform === "win32"
|
|
68
|
+
? "dir /ad"
|
|
69
|
+
: "tree -d -L 3 || find . -type d -maxdepth 3";
|
|
70
|
+
require("child_process").execSync(cmd, { stdio: "inherit", shell: true });
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error(
|
|
73
|
+
`${COLORS.RED}❌ Structure display failed:${COLORS.NC} ${error.message}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
checkProjectExists: (name) => {
|
|
79
|
+
return fs.existsSync(path.join(process.cwd(), name));
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
async function setupProject() {
|
|
84
|
+
try {
|
|
85
|
+
while (true) {
|
|
86
|
+
const { project } = await prompts({
|
|
87
|
+
type: "text",
|
|
88
|
+
name: "project",
|
|
89
|
+
message: "Enter your project name:",
|
|
90
|
+
validate: utils.validateName,
|
|
91
|
+
hint: "Use lowercase with hyphens (e.g., my-project)",
|
|
92
|
+
onState: (state) => {
|
|
93
|
+
if (state.aborted) {
|
|
94
|
+
console.log(`${COLORS.CYAN}Exiting setup process${COLORS.NC}`);
|
|
95
|
+
console.clear();
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (!project) continue;
|
|
102
|
+
|
|
103
|
+
if (utils.checkProjectExists(project)) {
|
|
104
|
+
console.error(
|
|
105
|
+
`${COLORS.RED}❌ Project '${project}' already exists${COLORS.NC}`
|
|
106
|
+
);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Get module name for go.mod
|
|
111
|
+
const { moduleName } = await prompts({
|
|
112
|
+
type: "text",
|
|
113
|
+
name: "moduleName",
|
|
114
|
+
message: "Enter Go module name (e.g., github.com/username/project):",
|
|
115
|
+
initial: `github.com/example/${project}`,
|
|
116
|
+
onState: (state) => {
|
|
117
|
+
if (state.aborted) {
|
|
118
|
+
console.log(`${COLORS.CYAN}Exiting setup process${COLORS.NC}`);
|
|
119
|
+
console.clear();
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (!moduleName) continue;
|
|
126
|
+
|
|
127
|
+
console.log(`
|
|
128
|
+
${COLORS.CYAN}✨ Project Details ✨${COLORS.NC}
|
|
129
|
+
├─ 📋 ${COLORS.YELLOW}Name:${COLORS.NC} ${project}
|
|
130
|
+
├─ 📦 ${COLORS.YELLOW}Module:${COLORS.NC} ${moduleName}
|
|
131
|
+
├─ 🚀 ${COLORS.YELLOW}Framework:${COLORS.NC} Gin-Gonic
|
|
132
|
+
└─ 💾 ${COLORS.YELLOW}Database:${COLORS.NC} PostgreSQL (GORM)
|
|
133
|
+
`);
|
|
134
|
+
|
|
135
|
+
const ProjectSetup = require(scripts.setup);
|
|
136
|
+
const setup = new ProjectSetup(project, moduleName);
|
|
137
|
+
await setup.execute();
|
|
138
|
+
|
|
139
|
+
console.log(`\n🎉 Project setup complete!`);
|
|
140
|
+
console.log(`\n🚀 To get started:`);
|
|
141
|
+
console.log(` ${COLORS.CYAN}cd ${project}${COLORS.NC}`);
|
|
142
|
+
console.log(` ${COLORS.CYAN}go mod tidy${COLORS.NC}`);
|
|
143
|
+
console.log(` ${COLORS.CYAN}go run src/main.go${COLORS.NC}`);
|
|
144
|
+
process.exit(0);
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(`${COLORS.RED}❌ Setup failed:${COLORS.NC} ${error.message}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function generateResource() {
|
|
152
|
+
try {
|
|
153
|
+
const { name } = await prompts({
|
|
154
|
+
type: "text",
|
|
155
|
+
name: "name",
|
|
156
|
+
message: `Enter resource name:`,
|
|
157
|
+
validate: utils.validateName,
|
|
158
|
+
hint: "Use lowercase with hyphens (e.g., user-profile)",
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (!name) return;
|
|
162
|
+
const folders = getRepositoryFolders();
|
|
163
|
+
const findmodule = folders.find((folder) => folder === name);
|
|
164
|
+
if (findmodule) {
|
|
165
|
+
console.error(
|
|
166
|
+
`${COLORS.RED}❌ Resource '${name}' already exists${COLORS.NC}`
|
|
167
|
+
);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const names = utils.processName(name);
|
|
172
|
+
const ModuleGenerator = require(scripts.createResource);
|
|
173
|
+
const generator = new ModuleGenerator(
|
|
174
|
+
names.lower,
|
|
175
|
+
names.pascal,
|
|
176
|
+
names.camel,
|
|
177
|
+
names.snake
|
|
178
|
+
);
|
|
179
|
+
await generator.execute();
|
|
180
|
+
return;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error(
|
|
183
|
+
`${COLORS.RED}❌ Resource setup failed:${COLORS.NC} ${error.message}`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function generateService() {
|
|
189
|
+
try {
|
|
190
|
+
const ServiceGenerator = require(scripts.createService);
|
|
191
|
+
const generator = new ServiceGenerator();
|
|
192
|
+
await generator.execute();
|
|
193
|
+
return;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error(
|
|
196
|
+
`${COLORS.RED}❌ Service setup failed:${COLORS.NC} ${error.message}`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function generateAuth() {
|
|
202
|
+
try {
|
|
203
|
+
const AuthGenerator = require(scripts.createAuth);
|
|
204
|
+
const generator = new AuthGenerator();
|
|
205
|
+
await generator.execute();
|
|
206
|
+
return;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error(
|
|
209
|
+
`${COLORS.RED}❌ Auth setup failed:${COLORS.NC} ${error.message}`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function addAuthToResource() {
|
|
215
|
+
try {
|
|
216
|
+
const folders = getRepositoryFolders();
|
|
217
|
+
if (folders[0].startsWith("No") || folders[0].startsWith("Error")) {
|
|
218
|
+
console.log(`${COLORS.YELLOW}${folders[0]}${COLORS.NC}`);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Filter out 'auth' folder since it's not a resource
|
|
223
|
+
const resourceFolders = folders.filter((f) => f !== "auth");
|
|
224
|
+
if (resourceFolders.length === 0) {
|
|
225
|
+
console.log(
|
|
226
|
+
`${COLORS.YELLOW}No resources found to add authentication${COLORS.NC}`,
|
|
227
|
+
);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const { selectedResource } = await prompts({
|
|
232
|
+
type: "select",
|
|
233
|
+
name: "selectedResource",
|
|
234
|
+
message: "Select a resource to add authentication:",
|
|
235
|
+
choices: resourceFolders.map((folder) => ({
|
|
236
|
+
title: folder,
|
|
237
|
+
value: folder,
|
|
238
|
+
})),
|
|
239
|
+
initial: 0,
|
|
240
|
+
onState: (state) => {
|
|
241
|
+
if (state.aborted) {
|
|
242
|
+
console.log(`${COLORS.CYAN}Exiting...${COLORS.NC}`);
|
|
243
|
+
console.clear();
|
|
244
|
+
process.exit(0);
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
if (!selectedResource) {
|
|
250
|
+
console.log(`${COLORS.CYAN}Selection cancelled${COLORS.NC}`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const names = utils.processName(selectedResource);
|
|
255
|
+
const AddAuthToResource = require(scripts.addAuthToResource);
|
|
256
|
+
const generator = new AddAuthToResource(
|
|
257
|
+
names.lower,
|
|
258
|
+
names.pascal,
|
|
259
|
+
names.camel,
|
|
260
|
+
);
|
|
261
|
+
await generator.execute();
|
|
262
|
+
} catch (error) {
|
|
263
|
+
console.error(
|
|
264
|
+
`${COLORS.RED}❌ Add auth failed:${COLORS.NC} ${error.message}`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Function to List All Modules from Folder (matching NestJS structure)
|
|
270
|
+
const getRepositoryFolders = () => {
|
|
271
|
+
try {
|
|
272
|
+
const repoPath = "src/infrastructure/repositories";
|
|
273
|
+
|
|
274
|
+
if (!fs.existsSync(repoPath)) {
|
|
275
|
+
return ["No resource found"];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const folders = fs.readdirSync(repoPath, { withFileTypes: true })
|
|
279
|
+
.filter(dirent => dirent.isDirectory())
|
|
280
|
+
.map(dirent => dirent.name)
|
|
281
|
+
.filter(folder => folder !== "base");
|
|
282
|
+
|
|
283
|
+
return folders.length > 0 ? folders : ["No resource found"];
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error(
|
|
286
|
+
`${COLORS.RED}❌ Error listing resource:${COLORS.NC} ${error.message}`
|
|
287
|
+
);
|
|
288
|
+
return ["Error listing resource"];
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const listModules = async () => {
|
|
293
|
+
const folders = getRepositoryFolders();
|
|
294
|
+
if (folders[0].startsWith("No") || folders[0].startsWith("Error")) {
|
|
295
|
+
console.log(`${COLORS.YELLOW}${folders[0]}${COLORS.NC}`);
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const { selectedRepo } = await prompts({
|
|
300
|
+
type: "select",
|
|
301
|
+
name: "selectedRepo",
|
|
302
|
+
message: "Select a resource to remove:",
|
|
303
|
+
choices: folders.map((folder) => ({
|
|
304
|
+
title: folder,
|
|
305
|
+
value: folder,
|
|
306
|
+
})),
|
|
307
|
+
initial: 0,
|
|
308
|
+
onState: (state) => {
|
|
309
|
+
if (state.aborted) {
|
|
310
|
+
console.log(`${COLORS.CYAN}Exiting setup process${COLORS.NC}`);
|
|
311
|
+
console.clear();
|
|
312
|
+
process.exit(0);
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
if (!selectedRepo) {
|
|
317
|
+
console.log(`${COLORS.CYAN}Selection cancelled${COLORS.NC}`);
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return selectedRepo;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// Command-line mode
|
|
325
|
+
async function commandLineMode() {
|
|
326
|
+
if (args[0] === "-v" || args[0] === "--version") {
|
|
327
|
+
displayVersion();
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (args[0] === "-h" || args[0] === "--help") {
|
|
332
|
+
require(scripts.help);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
switch (args[0]) {
|
|
338
|
+
case "g":
|
|
339
|
+
if (!args[1])
|
|
340
|
+
throw new Error("Usage: gcg g [res|resource|s|service|au|auth]");
|
|
341
|
+
if (args[1] === "res" || args[1] === "resource") {
|
|
342
|
+
await generateResource();
|
|
343
|
+
} else if (args[1] === "s" || args[1] === "service") {
|
|
344
|
+
await generateService();
|
|
345
|
+
} else if (args[1] === "au" || args[1] === "auth") {
|
|
346
|
+
await generateAuth();
|
|
347
|
+
} else {
|
|
348
|
+
throw new Error("Usage: gcg g [res|resource|s|service|au|auth]");
|
|
349
|
+
}
|
|
350
|
+
break;
|
|
351
|
+
|
|
352
|
+
case "rm":
|
|
353
|
+
if (!args[1]) throw new Error("Usage: gcg rm res");
|
|
354
|
+
if (args[1] === "res" || args[1] === "resource") {
|
|
355
|
+
const moduleName = await listModules();
|
|
356
|
+
if (moduleName) {
|
|
357
|
+
const rmNames = utils.processName(moduleName);
|
|
358
|
+
await require(scripts.removeModule)(
|
|
359
|
+
rmNames.lower,
|
|
360
|
+
rmNames.pascal,
|
|
361
|
+
rmNames.camel,
|
|
362
|
+
rmNames.snake,
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
} else {
|
|
366
|
+
throw new Error("Usage: gcg rm res");
|
|
367
|
+
}
|
|
368
|
+
break;
|
|
369
|
+
|
|
370
|
+
case "list":
|
|
371
|
+
case "ls":
|
|
372
|
+
const selected = await listModules();
|
|
373
|
+
if (selected) {
|
|
374
|
+
console.log(`${COLORS.GREEN}Selected resource: ${selected}`);
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
377
|
+
|
|
378
|
+
case "new":
|
|
379
|
+
case "n":
|
|
380
|
+
if (args[1]) throw new Error("'new' takes no arguments");
|
|
381
|
+
await setupProject();
|
|
382
|
+
break;
|
|
383
|
+
|
|
384
|
+
case "add":
|
|
385
|
+
if (!args[1]) throw new Error("Usage: gcg add [au|auth]");
|
|
386
|
+
if (args[1] === "au" || args[1] === "auth") {
|
|
387
|
+
await addAuthToResource();
|
|
388
|
+
} else {
|
|
389
|
+
throw new Error("Usage: gcg add [au|auth]");
|
|
390
|
+
}
|
|
391
|
+
break;
|
|
392
|
+
|
|
393
|
+
default:
|
|
394
|
+
throw new Error(`Unknown command: ${args[0]}`);
|
|
395
|
+
}
|
|
396
|
+
} catch (error) {
|
|
397
|
+
console.error(`${COLORS.RED}❌ Error: ${error.message}${COLORS.NC}`);
|
|
398
|
+
console.log(`${COLORS.CYAN}Use --help for command info${COLORS.NC}`);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Run with error handling
|
|
404
|
+
if (require.main === module) {
|
|
405
|
+
commandLineMode().catch((error) => {
|
|
406
|
+
console.error(`${COLORS.RED}❌ Fatal error:${COLORS.NC} ${error.message}`);
|
|
407
|
+
process.stdout.write("\x1B[?25h");
|
|
408
|
+
process.exit(1);
|
|
409
|
+
});
|
|
410
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
|
|
3
|
+
const COLORS = {
|
|
4
|
+
GREEN: "\x1b[32m",
|
|
5
|
+
YELLOW: "\x1b[33m",
|
|
6
|
+
RED: "\x1b[31m",
|
|
7
|
+
CYAN: "\x1b[36m",
|
|
8
|
+
MAGENTA: "\x1b[35m",
|
|
9
|
+
NC: "\x1b[0m",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const CONFIG = {
|
|
13
|
+
MODULE_NAME: "",
|
|
14
|
+
MODULE_NAME_PASCAL: "",
|
|
15
|
+
MODULE_NAME_CAMEL: "",
|
|
16
|
+
MODULE_NAME_SNAKE: "",
|
|
17
|
+
ROOT_DIR: "",
|
|
18
|
+
GO_MODULE: "",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
COLORS,
|
|
23
|
+
CONFIG,
|
|
24
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const fs = require("fs").promises;
|
|
2
|
+
|
|
3
|
+
const COLORS = {
|
|
4
|
+
GREEN: "\x1b[32m",
|
|
5
|
+
YELLOW: "\x1b[33m",
|
|
6
|
+
RED: "\x1b[31m",
|
|
7
|
+
NC: "\x1b[0m",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
async function createDir(dirPath) {
|
|
11
|
+
try {
|
|
12
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
13
|
+
console.log(`${COLORS.GREEN}✔ Created directory ${dirPath}${COLORS.NC}`);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if (error.code !== "EEXIST") {
|
|
16
|
+
console.error(`${COLORS.RED}❌ Failed to create directory ${dirPath}: ${error.message}${COLORS.NC}`);
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = createDir;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const fs = require("fs").promises;
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
const COLORS = {
|
|
5
|
+
GREEN: "\x1b[32m",
|
|
6
|
+
YELLOW: "\x1b[33m",
|
|
7
|
+
RED: "\x1b[31m",
|
|
8
|
+
NC: "\x1b[0m",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
async function createFile(filePath, content) {
|
|
12
|
+
try {
|
|
13
|
+
const dir = path.dirname(filePath);
|
|
14
|
+
await fs.mkdir(dir, { recursive: true });
|
|
15
|
+
await fs.writeFile(filePath, content, "utf8");
|
|
16
|
+
console.log(`${COLORS.GREEN}✔ Created ${filePath}${COLORS.NC}`);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error(`${COLORS.RED}❌ Failed to create ${filePath}: ${error.message}${COLORS.NC}`);
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = createFile;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs").promises;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { COLORS } = require("../constants/index");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* AddAuthToResource - Adds JWT authentication guard to an existing Gin controller
|
|
9
|
+
*/
|
|
10
|
+
class AddAuthToResource {
|
|
11
|
+
constructor(moduleNameLower, moduleNamePascal, moduleNameCamel) {
|
|
12
|
+
this.moduleNameLower = moduleNameLower;
|
|
13
|
+
this.moduleNamePascal = moduleNamePascal;
|
|
14
|
+
this.moduleNameCamel = moduleNameCamel;
|
|
15
|
+
|
|
16
|
+
this.srcDir = "src";
|
|
17
|
+
this.infrastructureDir = path.join(this.srcDir, "infrastructure");
|
|
18
|
+
|
|
19
|
+
// Get go module name from go.mod
|
|
20
|
+
this.goModule = this.getGoModuleName();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
getGoModuleName() {
|
|
24
|
+
try {
|
|
25
|
+
const goModPath = path.join(process.cwd(), "go.mod");
|
|
26
|
+
const content = require("fs").readFileSync(goModPath, "utf8");
|
|
27
|
+
const match = content.match(/module\s+(.+)/);
|
|
28
|
+
return match ? match[1].trim() : "github.com/example/project";
|
|
29
|
+
} catch {
|
|
30
|
+
return "github.com/example/project";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async execute() {
|
|
35
|
+
try {
|
|
36
|
+
console.log(
|
|
37
|
+
`${COLORS.YELLOW}Adding authentication to ${this.moduleNamePascal} resource...${COLORS.NC}`,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Check if auth module exists
|
|
41
|
+
const authDir = path.join(this.infrastructureDir, "common", "auth");
|
|
42
|
+
try {
|
|
43
|
+
await fs.access(authDir);
|
|
44
|
+
} catch {
|
|
45
|
+
console.log(
|
|
46
|
+
`${COLORS.RED}❌ Authentication module not found. Run 'gcg g au' first.${COLORS.NC}`,
|
|
47
|
+
);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await this.updateController();
|
|
52
|
+
await this.updateRouter();
|
|
53
|
+
|
|
54
|
+
console.log(
|
|
55
|
+
`${COLORS.GREEN}✔ Authentication added to ${this.moduleNamePascal} resource!${COLORS.NC}`,
|
|
56
|
+
);
|
|
57
|
+
this.printSummary();
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(`${COLORS.RED}Error:${COLORS.NC} ${error.message}`);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async updateController() {
|
|
65
|
+
const controllerPath = path.join(
|
|
66
|
+
this.infrastructureDir,
|
|
67
|
+
"controllers",
|
|
68
|
+
this.moduleNameLower,
|
|
69
|
+
`${this.moduleNameLower}.controller.go`,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
let content = await fs.readFile(controllerPath, "utf8");
|
|
74
|
+
|
|
75
|
+
// Check if already has auth middleware import
|
|
76
|
+
if (
|
|
77
|
+
content.includes("middleware.GetAuthUser") ||
|
|
78
|
+
content.includes("middleware.AuthMiddleware")
|
|
79
|
+
) {
|
|
80
|
+
console.log(
|
|
81
|
+
`${COLORS.YELLOW}⚠ ${this.moduleNamePascal} controller already has authentication${COLORS.NC}`,
|
|
82
|
+
);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Add middleware import if not present
|
|
87
|
+
if (
|
|
88
|
+
!content.includes(
|
|
89
|
+
`"${this.goModule}/src/infrastructure/common/middleware"`,
|
|
90
|
+
)
|
|
91
|
+
) {
|
|
92
|
+
content = content.replace(
|
|
93
|
+
/import \(\n/,
|
|
94
|
+
`import (\n\t"${this.goModule}/src/infrastructure/common/middleware"\n`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Add helper methods for auth
|
|
99
|
+
const authHelperFunc = `
|
|
100
|
+
// getAuthUser retrieves the authenticated user from Gin context
|
|
101
|
+
func (c *Controller) getAuthUser(ctx *gin.Context) (*models.AuthUser, bool) {
|
|
102
|
+
return middleware.GetAuthUser(ctx)
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
// Add before the Converters section or at the end of the file
|
|
107
|
+
if (
|
|
108
|
+
content.includes(
|
|
109
|
+
"// ============================================================================\n// Converters",
|
|
110
|
+
)
|
|
111
|
+
) {
|
|
112
|
+
content = content.replace(
|
|
113
|
+
"// ============================================================================\n// Converters",
|
|
114
|
+
authHelperFunc +
|
|
115
|
+
"\n// ============================================================================\n// Converters",
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await fs.writeFile(controllerPath, content, "utf8");
|
|
120
|
+
console.log(`${COLORS.GREEN}✔ Updated ${controllerPath}${COLORS.NC}`);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error(
|
|
123
|
+
`${COLORS.YELLOW}⚠ Could not update controller: ${error.message}${COLORS.NC}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async updateRouter() {
|
|
129
|
+
const routerPath = path.join(
|
|
130
|
+
this.infrastructureDir,
|
|
131
|
+
"controllers",
|
|
132
|
+
"router.go",
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
let content = await fs.readFile(routerPath, "utf8");
|
|
137
|
+
|
|
138
|
+
// Check if auth middleware import exists
|
|
139
|
+
if (
|
|
140
|
+
!content.includes(
|
|
141
|
+
`"${this.goModule}/src/infrastructure/common/middleware"`,
|
|
142
|
+
)
|
|
143
|
+
) {
|
|
144
|
+
content = content.replace(
|
|
145
|
+
/import \(\n/,
|
|
146
|
+
`import (\n\t"${this.goModule}/src/infrastructure/common/middleware"\n`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Check if auth import exists
|
|
151
|
+
if (
|
|
152
|
+
!content.includes(`"${this.goModule}/src/infrastructure/common/auth"`)
|
|
153
|
+
) {
|
|
154
|
+
content = content.replace(
|
|
155
|
+
/import \(\n/,
|
|
156
|
+
`import (\n\t"${this.goModule}/src/infrastructure/common/auth"\n`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check if this module's routes already use auth middleware
|
|
161
|
+
const authMiddlewarePattern = new RegExp(
|
|
162
|
+
`${this.moduleNameLower}s?\\.Use\\(middleware\\.AuthMiddleware`,
|
|
163
|
+
"m",
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
if (authMiddlewarePattern.test(content)) {
|
|
167
|
+
console.log(
|
|
168
|
+
`${COLORS.YELLOW}⚠ Router already has auth middleware for ${this.moduleNamePascal}${COLORS.NC}`,
|
|
169
|
+
);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Find the module's route group and add auth middleware
|
|
174
|
+
// Pattern: moduleName := v1.Group("/moduleNames")
|
|
175
|
+
const groupPattern = new RegExp(
|
|
176
|
+
`(${this.moduleNameLower}s?\\s*:=\\s*v1\\.Group\\(["\']\/${this.moduleNameLower}s?["\']\\))`,
|
|
177
|
+
"m",
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const match = content.match(groupPattern);
|
|
181
|
+
if (match) {
|
|
182
|
+
// Add auth middleware after the group declaration
|
|
183
|
+
const authMiddleware = `$1\n\t${this.moduleNameLower}s.Use(middleware.AuthMiddleware(auth.NewJWTService()))`;
|
|
184
|
+
content = content.replace(groupPattern, authMiddleware);
|
|
185
|
+
|
|
186
|
+
await fs.writeFile(routerPath, content, "utf8");
|
|
187
|
+
console.log(
|
|
188
|
+
`${COLORS.GREEN}✔ Updated ${routerPath} with auth middleware${COLORS.NC}`,
|
|
189
|
+
);
|
|
190
|
+
} else {
|
|
191
|
+
console.log(
|
|
192
|
+
`${COLORS.YELLOW}⚠ Could not find ${this.moduleNameLower} route group in router.go. Please add auth middleware manually.${COLORS.NC}`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.error(
|
|
197
|
+
`${COLORS.YELLOW}⚠ Could not update router: ${error.message}${COLORS.NC}`,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
printSummary() {
|
|
203
|
+
console.log(`
|
|
204
|
+
${COLORS.CYAN}╔════════════════════════════════════════════════════════════╗
|
|
205
|
+
║ Authentication Added to Resource ║
|
|
206
|
+
╠════════════════════════════════════════════════════════════╣
|
|
207
|
+
║ 📁 Updated: ║
|
|
208
|
+
║ • infrastructure/controllers/${this.moduleNameLower}/${this.moduleNameLower}.controller.go
|
|
209
|
+
║ • infrastructure/controllers/router.go ║
|
|
210
|
+
║ ║
|
|
211
|
+
║ 🔧 New helper methods available: ║
|
|
212
|
+
║ • c.getAuthUser(ctx) - Get authenticated user ║
|
|
213
|
+
║ ║
|
|
214
|
+
║ 📝 Usage in your controller methods: ║
|
|
215
|
+
║ user, ok := c.getAuthUser(ctx) ║
|
|
216
|
+
║ if !ok { response.Error(ctx, 401, "Unauthorized") } ║
|
|
217
|
+
║ // Use user.ID, user.Email, user.Role ║
|
|
218
|
+
║ ║
|
|
219
|
+
║ 🔒 Auth middleware automatically added to ${this.moduleNameLower} routes ║
|
|
220
|
+
╚════════════════════════════════════════════════════════════╝${COLORS.NC}
|
|
221
|
+
`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
module.exports = AddAuthToResource;
|