lapeh 2.2.7 ā 2.2.8
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/bin/index.js +164 -13
- package/package.json +6 -6
- package/api-testing-sepuluh/.env.example +0 -19
- package/api-testing-sepuluh/doc/ARCHITECTURE_GUIDE.md +0 -73
- package/api-testing-sepuluh/doc/CHANGELOG.md +0 -77
- package/api-testing-sepuluh/doc/CHEATSHEET.md +0 -94
- package/api-testing-sepuluh/doc/CLI.md +0 -106
- package/api-testing-sepuluh/doc/CONTRIBUTING.md +0 -105
- package/api-testing-sepuluh/doc/DEPLOYMENT.md +0 -122
- package/api-testing-sepuluh/doc/FAQ.md +0 -81
- package/api-testing-sepuluh/doc/FEATURES.md +0 -165
- package/api-testing-sepuluh/doc/GETTING_STARTED.md +0 -108
- package/api-testing-sepuluh/doc/INTRODUCTION.md +0 -60
- package/api-testing-sepuluh/doc/PACKAGES.md +0 -66
- package/api-testing-sepuluh/doc/PERFORMANCE.md +0 -91
- package/api-testing-sepuluh/doc/ROADMAP.md +0 -93
- package/api-testing-sepuluh/doc/SECURITY.md +0 -93
- package/api-testing-sepuluh/doc/STRUCTURE.md +0 -90
- package/api-testing-sepuluh/doc/TUTORIAL.md +0 -192
- package/api-testing-sepuluh/docker-compose.yml +0 -24
- package/api-testing-sepuluh/eslint.config.mjs +0 -26
- package/api-testing-sepuluh/framework.md +0 -168
- package/api-testing-sepuluh/nodemon.json +0 -6
- package/api-testing-sepuluh/package-lock.json +0 -5539
- package/api-testing-sepuluh/package.json +0 -103
- package/api-testing-sepuluh/prisma/base.prisma.template +0 -7
- package/api-testing-sepuluh/prisma/migrations/20251227034737_init_setup/migration.sql +0 -248
- package/api-testing-sepuluh/prisma/migrations/migration_lock.toml +0 -3
- package/api-testing-sepuluh/prisma/schema.prisma +0 -183
- package/api-testing-sepuluh/prisma/seed.ts +0 -411
- package/api-testing-sepuluh/prisma.config.ts +0 -15
- package/api-testing-sepuluh/readme.md +0 -414
- package/api-testing-sepuluh/scripts/check-update.js +0 -92
- package/api-testing-sepuluh/scripts/compile-schema.js +0 -29
- package/api-testing-sepuluh/scripts/config-clear.js +0 -45
- package/api-testing-sepuluh/scripts/generate-jwt-secret.js +0 -38
- package/api-testing-sepuluh/scripts/init-project.js +0 -178
- package/api-testing-sepuluh/scripts/make-controller.js +0 -205
- package/api-testing-sepuluh/scripts/make-model.js +0 -42
- package/api-testing-sepuluh/scripts/make-module.js +0 -158
- package/api-testing-sepuluh/scripts/verify-rbac-functional.js +0 -187
- package/api-testing-sepuluh/src/controllers/authController.ts +0 -469
- package/api-testing-sepuluh/src/controllers/petController.ts +0 -194
- package/api-testing-sepuluh/src/controllers/rbacController.ts +0 -478
- package/api-testing-sepuluh/src/models/core.prisma +0 -163
- package/api-testing-sepuluh/src/models/pets.prisma +0 -9
- package/api-testing-sepuluh/src/routes/auth.ts +0 -74
- package/api-testing-sepuluh/src/routes/index.ts +0 -10
- package/api-testing-sepuluh/src/routes/pets.ts +0 -13
- package/api-testing-sepuluh/src/routes/rbac.ts +0 -42
- package/api-testing-sepuluh/storage/logs/.gitkeep +0 -0
- package/api-testing-sepuluh/tsconfig.json +0 -39
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
const { execSync } = require("child_process");
|
|
4
|
-
const readline = require("readline");
|
|
5
|
-
|
|
6
|
-
const rootDir = path.join(__dirname, "..");
|
|
7
|
-
const envExample = path.join(rootDir, ".env.example");
|
|
8
|
-
const envFile = path.join(rootDir, ".env");
|
|
9
|
-
const prismaBaseFile = path.join(rootDir, "prisma", "base.prisma.template");
|
|
10
|
-
|
|
11
|
-
const rl = readline.createInterface({
|
|
12
|
-
input: process.stdin,
|
|
13
|
-
output: process.stdout,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const ask = (query, defaultVal) => {
|
|
17
|
-
return new Promise((resolve) => {
|
|
18
|
-
rl.question(`${query} ${defaultVal ? `[${defaultVal}]` : ""}: `, (answer) => {
|
|
19
|
-
resolve(answer.trim() || defaultVal);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const selectOption = async (query, options) => {
|
|
25
|
-
console.log(query);
|
|
26
|
-
options.forEach((opt, idx) => {
|
|
27
|
-
console.log(` [${opt.key}] ${opt.label}`);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
while (true) {
|
|
31
|
-
const answer = await ask(">", options[0].key); // Default to first option
|
|
32
|
-
const selected = options.find(o => o.key.toLowerCase() === answer.toLowerCase());
|
|
33
|
-
if (selected) return selected;
|
|
34
|
-
|
|
35
|
-
// Check if user entered the full name or label
|
|
36
|
-
const byLabel = options.find(o => o.label.toLowerCase().includes(answer.toLowerCase()));
|
|
37
|
-
if (byLabel) return byLabel;
|
|
38
|
-
|
|
39
|
-
console.log("Pilihan tidak valid. Silakan coba lagi.");
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
(async () => {
|
|
44
|
-
console.log("š Starting project initialization...");
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
// --- DATABASE SELECTION ---
|
|
48
|
-
console.log("\n--- Database Configuration ---");
|
|
49
|
-
const dbType = await selectOption("Database apa yang akan digunakan?", [
|
|
50
|
-
{ key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" },
|
|
51
|
-
{ key: "mysql", label: "MySQL", provider: "mysql", defaultPort: "3306" },
|
|
52
|
-
]);
|
|
53
|
-
|
|
54
|
-
let dbUrl = "";
|
|
55
|
-
let dbProvider = dbType.provider;
|
|
56
|
-
|
|
57
|
-
const host = await ask("Database Host", "localhost");
|
|
58
|
-
const port = await ask("Database Port", dbType.defaultPort);
|
|
59
|
-
const user = await ask("Database User", "root");
|
|
60
|
-
const password = await ask("Database Password", "");
|
|
61
|
-
const dbName = await ask("Database Name", "lapeh");
|
|
62
|
-
|
|
63
|
-
if (dbType.key === "pgsql") {
|
|
64
|
-
dbUrl = `postgresql://${user}:${password}@${host}:${port}/${dbName}?schema=public`;
|
|
65
|
-
} else {
|
|
66
|
-
dbUrl = `mysql://${user}:${password}@${host}:${port}/${dbName}`;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Close readline as we are done with input
|
|
70
|
-
rl.close();
|
|
71
|
-
|
|
72
|
-
// 1. Setup .env
|
|
73
|
-
console.log("\nš Setting up .env...");
|
|
74
|
-
let envContent = "";
|
|
75
|
-
if (fs.existsSync(envExample)) {
|
|
76
|
-
envContent = fs.readFileSync(envExample, "utf8");
|
|
77
|
-
} else {
|
|
78
|
-
// Fallback minimal env if example missing
|
|
79
|
-
envContent = `PORT=4000\nDATABASE_PROVIDER="postgresql"\nDATABASE_URL=""\nJWT_SECRET="replace_this"\n`;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Replace DATABASE_URL and DATABASE_PROVIDER
|
|
83
|
-
// Regex to replace existing values or append if missing (simplified)
|
|
84
|
-
if (envContent.includes("DATABASE_URL=")) {
|
|
85
|
-
envContent = envContent.replace(/DATABASE_URL=".+"/g, `DATABASE_URL="${dbUrl}"`);
|
|
86
|
-
envContent = envContent.replace(/DATABASE_URL=.+/g, `DATABASE_URL="${dbUrl}"`); // Handle unquoted
|
|
87
|
-
} else {
|
|
88
|
-
envContent += `\nDATABASE_URL="${dbUrl}"`;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (envContent.includes("DATABASE_PROVIDER=")) {
|
|
92
|
-
envContent = envContent.replace(/DATABASE_PROVIDER=".+"/g, `DATABASE_PROVIDER="${dbProvider}"`);
|
|
93
|
-
envContent = envContent.replace(/DATABASE_PROVIDER=.+/g, `DATABASE_PROVIDER="${dbProvider}"`);
|
|
94
|
-
} else {
|
|
95
|
-
envContent += `\nDATABASE_PROVIDER="${dbProvider}"`;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
fs.writeFileSync(envFile, envContent);
|
|
99
|
-
console.log("ā
.env updated with database configuration.");
|
|
100
|
-
|
|
101
|
-
// 2. Update prisma/base.prisma.template
|
|
102
|
-
console.log("š Updating prisma/base.prisma.template...");
|
|
103
|
-
if (fs.existsSync(prismaBaseFile)) {
|
|
104
|
-
let baseContent = fs.readFileSync(prismaBaseFile, "utf8");
|
|
105
|
-
// Replace provider in datasource block
|
|
106
|
-
baseContent = baseContent.replace(
|
|
107
|
-
/(datasource\s+db\s+\{[\s\S]*?provider\s*=\s*")([^"]+)(")/,
|
|
108
|
-
`$1${dbProvider}$3`
|
|
109
|
-
);
|
|
110
|
-
fs.writeFileSync(prismaBaseFile, baseContent);
|
|
111
|
-
} else {
|
|
112
|
-
console.warn("ā ļø prisma/base.prisma.template not found. Skipping.");
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// 3. Install dependencies
|
|
116
|
-
console.log("\nš¦ Installing dependencies...");
|
|
117
|
-
execSync("npm install", { stdio: "inherit", cwd: rootDir });
|
|
118
|
-
|
|
119
|
-
// 4. Create .vscode/settings.json
|
|
120
|
-
console.log("\nš ļø Configuring VS Code...");
|
|
121
|
-
const vscodeDir = path.join(rootDir, ".vscode");
|
|
122
|
-
if (!fs.existsSync(vscodeDir)) {
|
|
123
|
-
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
124
|
-
}
|
|
125
|
-
const settingsFile = path.join(vscodeDir, "settings.json");
|
|
126
|
-
const settingsContent = {
|
|
127
|
-
"files.associations": {
|
|
128
|
-
"*.model": "prisma"
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
fs.writeFileSync(settingsFile, JSON.stringify(settingsContent, null, 2));
|
|
132
|
-
console.log("ā
VS Code configured (.model support added).");
|
|
133
|
-
|
|
134
|
-
// 5. Generate JWT Secret
|
|
135
|
-
console.log("\nš Generating JWT Secret...");
|
|
136
|
-
try {
|
|
137
|
-
execSync("node scripts/generate-jwt-secret.js", {
|
|
138
|
-
stdio: "inherit",
|
|
139
|
-
cwd: rootDir,
|
|
140
|
-
});
|
|
141
|
-
} catch (e) {
|
|
142
|
-
console.warn("ā ļø Failed to generate JWT secret automatically.");
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// 5. Setup Database (Migrate)
|
|
146
|
-
console.log("\nšļø Setting up database...");
|
|
147
|
-
try {
|
|
148
|
-
execSync("node scripts/compile-schema.js", { stdio: "inherit", cwd: rootDir });
|
|
149
|
-
console.log("āļø Generating Prisma Client...");
|
|
150
|
-
execSync("npx prisma generate", { stdio: "inherit", cwd: rootDir });
|
|
151
|
-
|
|
152
|
-
console.log("š Running Migration...");
|
|
153
|
-
execSync("npx prisma migrate dev --name init_setup", { stdio: "inherit", cwd: rootDir });
|
|
154
|
-
} catch (error) {
|
|
155
|
-
console.warn(
|
|
156
|
-
'ā ļø Database migration had an issue. Please check your database connection in .env and run "npm run prisma:migrate" manually.'
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// 6. Seed Database
|
|
161
|
-
console.log("\nš± Seeding database...");
|
|
162
|
-
try {
|
|
163
|
-
execSync("npm run db:seed", { stdio: "inherit", cwd: rootDir });
|
|
164
|
-
} catch (error) {
|
|
165
|
-
console.warn(
|
|
166
|
-
'ā ļø Database seeding had an issue. You might need to run "npm run db:seed" manually.'
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
console.log("\nā
Setup complete! You can now run:");
|
|
171
|
-
console.log(" npm run dev");
|
|
172
|
-
|
|
173
|
-
} catch (error) {
|
|
174
|
-
console.error("\nā Setup failed:", error.message);
|
|
175
|
-
rl.close();
|
|
176
|
-
process.exit(1);
|
|
177
|
-
}
|
|
178
|
-
})();
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
const args = process.argv.slice(2);
|
|
5
|
-
const controllerName = args[0];
|
|
6
|
-
const isResource = args.includes('-r') || args.includes('--resource');
|
|
7
|
-
|
|
8
|
-
if (!controllerName || controllerName.startsWith('-')) {
|
|
9
|
-
console.error('ā Please specify the controller name.');
|
|
10
|
-
console.error(' Usage: npm run make:controller <ControllerName> [-r]');
|
|
11
|
-
console.error(' Example: npm run make:controller TestController -r');
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const controllersDir = path.join(__dirname, '..', 'src', 'controllers');
|
|
16
|
-
|
|
17
|
-
// Ensure controllers directory exists
|
|
18
|
-
if (!fs.existsSync(controllersDir)) {
|
|
19
|
-
fs.mkdirSync(controllersDir, { recursive: true });
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
let fileName = controllerName;
|
|
23
|
-
if (!fileName.endsWith('.ts')) {
|
|
24
|
-
fileName += '.ts';
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Capitalize first letter if convention is needed, but usually users provide PascalCase
|
|
28
|
-
// We will just use what user provided but ensure .ts extension
|
|
29
|
-
const filePath = path.join(controllersDir, fileName);
|
|
30
|
-
|
|
31
|
-
if (fs.existsSync(filePath)) {
|
|
32
|
-
console.error(`ā Controller ${fileName} already exists at ${filePath}`);
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
let content = '';
|
|
37
|
-
|
|
38
|
-
if (isResource) {
|
|
39
|
-
content = `import { Request, Response } from "express";
|
|
40
|
-
import { prisma } from "../core/database";
|
|
41
|
-
import { sendSuccess, sendError } from "../utils/response";
|
|
42
|
-
import { getPagination, buildPaginationMeta } from "../utils/pagination";
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Display a listing of the resource.
|
|
46
|
-
*/
|
|
47
|
-
export async function index(req: Request, res: Response) {
|
|
48
|
-
const { page, perPage, skip, take } = getPagination(req.query);
|
|
49
|
-
|
|
50
|
-
// TODO: Add search logic
|
|
51
|
-
const where: any = {};
|
|
52
|
-
|
|
53
|
-
// TODO: Replace 'model' with your actual model name
|
|
54
|
-
/*
|
|
55
|
-
const [data, total] = await Promise.all([
|
|
56
|
-
prisma.model.findMany({
|
|
57
|
-
where,
|
|
58
|
-
skip,
|
|
59
|
-
take,
|
|
60
|
-
orderBy: { created_at: "desc" },
|
|
61
|
-
}),
|
|
62
|
-
prisma.model.count({ where }),
|
|
63
|
-
]);
|
|
64
|
-
|
|
65
|
-
const serialized = data.map((item: any) => ({
|
|
66
|
-
...item,
|
|
67
|
-
id: item.id.toString(),
|
|
68
|
-
}));
|
|
69
|
-
|
|
70
|
-
const meta = buildPaginationMeta(page, perPage, total);
|
|
71
|
-
|
|
72
|
-
sendSuccess(res, 200, "Data retrieved successfully", {
|
|
73
|
-
data: serialized,
|
|
74
|
-
meta,
|
|
75
|
-
});
|
|
76
|
-
*/
|
|
77
|
-
|
|
78
|
-
sendSuccess(res, 200, "Index method", { message: "Implement me" });
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Display the specified resource.
|
|
83
|
-
*/
|
|
84
|
-
export async function show(req: Request, res: Response) {
|
|
85
|
-
const { id } = req.params;
|
|
86
|
-
|
|
87
|
-
// TODO: Replace 'model' with your actual model name
|
|
88
|
-
/*
|
|
89
|
-
const item = await prisma.model.findUnique({
|
|
90
|
-
where: { id: BigInt(id) },
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
if (!item) {
|
|
94
|
-
sendError(res, 404, "Data not found");
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
sendSuccess(res, 200, "Data retrieved successfully", {
|
|
99
|
-
...item,
|
|
100
|
-
id: item.id.toString(),
|
|
101
|
-
});
|
|
102
|
-
*/
|
|
103
|
-
sendSuccess(res, 200, "Show method", { id, message: "Implement me" });
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Store a newly created resource in storage.
|
|
108
|
-
*/
|
|
109
|
-
export async function store(req: Request, res: Response) {
|
|
110
|
-
// TODO: Add validation
|
|
111
|
-
// const parsed = createSchema.safeParse(req.body);
|
|
112
|
-
// if (!parsed.success) { ... }
|
|
113
|
-
|
|
114
|
-
// TODO: Replace 'model' with your actual model name
|
|
115
|
-
/*
|
|
116
|
-
const item = await prisma.model.create({
|
|
117
|
-
data: {
|
|
118
|
-
...req.body,
|
|
119
|
-
created_at: new Date(),
|
|
120
|
-
updated_at: new Date(),
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
sendSuccess(res, 201, "Data created successfully", {
|
|
125
|
-
...item,
|
|
126
|
-
id: item.id.toString(),
|
|
127
|
-
});
|
|
128
|
-
*/
|
|
129
|
-
sendSuccess(res, 201, "Store method", { message: "Implement me" });
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Update the specified resource in storage.
|
|
134
|
-
*/
|
|
135
|
-
export async function update(req: Request, res: Response) {
|
|
136
|
-
const { id } = req.params;
|
|
137
|
-
|
|
138
|
-
// TODO: Add validation
|
|
139
|
-
// const parsed = updateSchema.safeParse(req.body);
|
|
140
|
-
|
|
141
|
-
// TODO: Replace 'model' with your actual model name
|
|
142
|
-
/*
|
|
143
|
-
const existing = await prisma.model.findUnique({
|
|
144
|
-
where: { id: BigInt(id) },
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
if (!existing) {
|
|
148
|
-
sendError(res, 404, "Data not found");
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const updated = await prisma.model.update({
|
|
153
|
-
where: { id: BigInt(id) },
|
|
154
|
-
data: {
|
|
155
|
-
...req.body,
|
|
156
|
-
updated_at: new Date(),
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
sendSuccess(res, 200, "Data updated successfully", {
|
|
161
|
-
...updated,
|
|
162
|
-
id: updated.id.toString(),
|
|
163
|
-
});
|
|
164
|
-
*/
|
|
165
|
-
sendSuccess(res, 200, "Update method", { id, message: "Implement me" });
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Remove the specified resource from storage.
|
|
170
|
-
*/
|
|
171
|
-
export async function destroy(req: Request, res: Response) {
|
|
172
|
-
const { id } = req.params;
|
|
173
|
-
|
|
174
|
-
// TODO: Replace 'model' with your actual model name
|
|
175
|
-
/*
|
|
176
|
-
const existing = await prisma.model.findUnique({
|
|
177
|
-
where: { id: BigInt(id) },
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
if (!existing) {
|
|
181
|
-
sendError(res, 404, "Data not found");
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
await prisma.model.delete({
|
|
186
|
-
where: { id: BigInt(id) },
|
|
187
|
-
});
|
|
188
|
-
*/
|
|
189
|
-
|
|
190
|
-
sendSuccess(res, 200, "Data deleted successfully", null);
|
|
191
|
-
}
|
|
192
|
-
`;
|
|
193
|
-
} else {
|
|
194
|
-
content = `import { Request, Response } from "express";
|
|
195
|
-
import { sendSuccess, sendError } from "../utils/response";
|
|
196
|
-
|
|
197
|
-
export async function index(req: Request, res: Response) {
|
|
198
|
-
sendSuccess(res, 200, "Hello from ${controllerName}", null);
|
|
199
|
-
}
|
|
200
|
-
`;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
fs.writeFileSync(filePath, content);
|
|
204
|
-
|
|
205
|
-
console.log(`ā
Controller created: src/controllers/${fileName}`);
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
const modelName = process.argv[2];
|
|
5
|
-
|
|
6
|
-
if (!modelName) {
|
|
7
|
-
console.error('ā Please specify the model name.');
|
|
8
|
-
console.error(' Usage: npm run make:model <ModelName>');
|
|
9
|
-
console.error(' Example: npm run make:model Product');
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const PascalCaseName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
|
|
14
|
-
const tableName = modelName.toLowerCase() + 's'; // simple pluralization
|
|
15
|
-
|
|
16
|
-
const modelsDir = path.join(__dirname, '..', 'src', 'models');
|
|
17
|
-
const modelPath = path.join(modelsDir, `${PascalCaseName}.prisma`);
|
|
18
|
-
|
|
19
|
-
if (fs.existsSync(modelPath)) {
|
|
20
|
-
console.error(`ā Model ${PascalCaseName} already exists at ${modelPath}`);
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Ensure models directory exists
|
|
25
|
-
if (!fs.existsSync(modelsDir)) {
|
|
26
|
-
fs.mkdirSync(modelsDir, { recursive: true });
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const content = `model ${tableName} {
|
|
30
|
-
id BigInt @id @default(autoincrement())
|
|
31
|
-
name String
|
|
32
|
-
createdAt DateTime? @default(now())
|
|
33
|
-
updatedAt DateTime? @updatedAt
|
|
34
|
-
}
|
|
35
|
-
`;
|
|
36
|
-
|
|
37
|
-
fs.writeFileSync(modelPath, content);
|
|
38
|
-
|
|
39
|
-
console.log(`ā
Model created: src/models/${PascalCaseName}.prisma`);
|
|
40
|
-
console.log(`\nNext steps:`);
|
|
41
|
-
console.log(`1. Edit the model file.`);
|
|
42
|
-
console.log(`2. Run 'npm run prisma:migrate' to update the database.`);
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
const moduleName = process.argv[2];
|
|
5
|
-
|
|
6
|
-
if (!moduleName) {
|
|
7
|
-
console.error('ā Please specify the module name.');
|
|
8
|
-
console.error(' Usage: npm run make:module <name>');
|
|
9
|
-
console.error(' Example: npm run make:module product');
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Convert "product" -> "Product" (PascalCase) for Class names
|
|
14
|
-
const PascalCaseName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1);
|
|
15
|
-
// Convert "product" -> "product" (camelCase) for variables/files
|
|
16
|
-
const camelCaseName = moduleName.toLowerCase();
|
|
17
|
-
|
|
18
|
-
const srcDir = path.join(__dirname, '..', 'src');
|
|
19
|
-
|
|
20
|
-
// 1. Create Controller
|
|
21
|
-
const controllerContent = `import { Request, Response } from 'express';
|
|
22
|
-
import { ${PascalCaseName}Service } from '../services/${camelCaseName}Service';
|
|
23
|
-
import { successResponse, errorResponse } from '../utils/response';
|
|
24
|
-
|
|
25
|
-
export const ${PascalCaseName}Controller = {
|
|
26
|
-
async getAll(req: Request, res: Response) {
|
|
27
|
-
try {
|
|
28
|
-
const data = await ${PascalCaseName}Service.getAll();
|
|
29
|
-
return successResponse(res, data, '${PascalCaseName}s retrieved successfully');
|
|
30
|
-
} catch (error) {
|
|
31
|
-
return errorResponse(res, error as Error);
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
async getById(req: Request, res: Response) {
|
|
36
|
-
try {
|
|
37
|
-
const { id } = req.params;
|
|
38
|
-
const data = await ${PascalCaseName}Service.getById(id);
|
|
39
|
-
if (!data) return errorResponse(res, new Error('${PascalCaseName} not found'), 404);
|
|
40
|
-
return successResponse(res, data, '${PascalCaseName} retrieved successfully');
|
|
41
|
-
} catch (error) {
|
|
42
|
-
return errorResponse(res, error as Error);
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
async create(req: Request, res: Response) {
|
|
47
|
-
try {
|
|
48
|
-
const data = await ${PascalCaseName}Service.create(req.body);
|
|
49
|
-
return successResponse(res, data, '${PascalCaseName} created successfully', 201);
|
|
50
|
-
} catch (error) {
|
|
51
|
-
return errorResponse(res, error as Error);
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
async update(req: Request, res: Response) {
|
|
56
|
-
try {
|
|
57
|
-
const { id } = req.params;
|
|
58
|
-
const data = await ${PascalCaseName}Service.update(id, req.body);
|
|
59
|
-
return successResponse(res, data, '${PascalCaseName} updated successfully');
|
|
60
|
-
} catch (error) {
|
|
61
|
-
return errorResponse(res, error as Error);
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
async delete(req: Request, res: Response) {
|
|
66
|
-
try {
|
|
67
|
-
const { id } = req.params;
|
|
68
|
-
await ${PascalCaseName}Service.delete(id);
|
|
69
|
-
return successResponse(res, null, '${PascalCaseName} deleted successfully');
|
|
70
|
-
} catch (error) {
|
|
71
|
-
return errorResponse(res, error as Error);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
`;
|
|
76
|
-
|
|
77
|
-
// 2. Create Service
|
|
78
|
-
const serviceContent = `// import prisma from '../prisma'; // Uncomment this line if you use Prisma
|
|
79
|
-
|
|
80
|
-
export const ${PascalCaseName}Service = {
|
|
81
|
-
async getAll() {
|
|
82
|
-
// return prisma.${camelCaseName}.findMany();
|
|
83
|
-
return [{ id: 1, name: 'Sample ${PascalCaseName}' }]; // Placeholder
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
async getById(id: string) {
|
|
87
|
-
// return prisma.${camelCaseName}.findUnique({ where: { id } });
|
|
88
|
-
return { id, name: 'Sample ${PascalCaseName}' }; // Placeholder
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
async create(data: any) {
|
|
92
|
-
// return prisma.${camelCaseName}.create({ data });
|
|
93
|
-
return { id: Date.now(), ...data }; // Placeholder
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
async update(id: string, data: any) {
|
|
97
|
-
// return prisma.${camelCaseName}.update({ where: { id }, data });
|
|
98
|
-
return { id, ...data }; // Placeholder
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
async delete(id: string) {
|
|
102
|
-
// return prisma.${camelCaseName}.delete({ where: { id } });
|
|
103
|
-
return true; // Placeholder
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
`;
|
|
107
|
-
|
|
108
|
-
// 3. Create Route
|
|
109
|
-
const routeContent = `import { Router } from 'express';
|
|
110
|
-
import { ${PascalCaseName}Controller } from '../controllers/${camelCaseName}Controller';
|
|
111
|
-
import { authenticateToken } from '../middleware/auth';
|
|
112
|
-
|
|
113
|
-
const router = Router();
|
|
114
|
-
|
|
115
|
-
router.get('/', authenticateToken, ${PascalCaseName}Controller.getAll);
|
|
116
|
-
router.get('/:id', authenticateToken, ${PascalCaseName}Controller.getById);
|
|
117
|
-
router.post('/', authenticateToken, ${PascalCaseName}Controller.create);
|
|
118
|
-
router.put('/:id', authenticateToken, ${PascalCaseName}Controller.update);
|
|
119
|
-
router.delete('/:id', authenticateToken, ${PascalCaseName}Controller.delete);
|
|
120
|
-
|
|
121
|
-
export default router;
|
|
122
|
-
`;
|
|
123
|
-
|
|
124
|
-
const paths = {
|
|
125
|
-
controller: path.join(srcDir, 'controllers', `${camelCaseName}Controller.ts`),
|
|
126
|
-
service: path.join(srcDir, 'services', `${camelCaseName}Service.ts`),
|
|
127
|
-
route: path.join(srcDir, 'routes', `${camelCaseName}.ts`),
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
// Helper to create directory if not exists
|
|
131
|
-
function ensureDir(filePath) {
|
|
132
|
-
const dirname = path.dirname(filePath);
|
|
133
|
-
if (!fs.existsSync(dirname)) {
|
|
134
|
-
fs.mkdirSync(dirname, { recursive: true });
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
ensureDir(paths.controller);
|
|
140
|
-
fs.writeFileSync(paths.controller, controllerContent);
|
|
141
|
-
console.log(`ā
Created Controller: src/controllers/${camelCaseName}Controller.ts`);
|
|
142
|
-
|
|
143
|
-
ensureDir(paths.service);
|
|
144
|
-
fs.writeFileSync(paths.service, serviceContent);
|
|
145
|
-
console.log(`ā
Created Service: src/services/${camelCaseName}Service.ts`);
|
|
146
|
-
|
|
147
|
-
ensureDir(paths.route);
|
|
148
|
-
fs.writeFileSync(paths.route, routeContent);
|
|
149
|
-
console.log(`ā
Created Route: src/routes/${camelCaseName}.ts`);
|
|
150
|
-
|
|
151
|
-
console.log('\nā ļø Don\'t forget to register the new route in src/index.ts or src/server.ts!');
|
|
152
|
-
console.log(` import ${camelCaseName}Routes from './routes/${camelCaseName}';`);
|
|
153
|
-
console.log(` app.use('/${camelCaseName}s', ${camelCaseName}Routes);`);
|
|
154
|
-
|
|
155
|
-
} catch (error) {
|
|
156
|
-
console.error('ā Error creating module:', error);
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|