lapeh 2.6.17 → 3.0.2

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.
Files changed (67) hide show
  1. package/.env.example +1 -6
  2. package/README.md +19 -85
  3. package/bin/index.js +84 -180
  4. package/dist/lib/bootstrap.d.ts.map +1 -1
  5. package/dist/lib/bootstrap.js +17 -16
  6. package/dist/lib/core/store.d.ts +55 -0
  7. package/dist/lib/core/store.d.ts.map +1 -0
  8. package/dist/lib/core/store.js +66 -0
  9. package/dist/lib/middleware/error.d.ts.map +1 -1
  10. package/dist/lib/middleware/error.js +1 -20
  11. package/dist/lib/utils/validator.d.ts.map +1 -1
  12. package/dist/lib/utils/validator.js +3 -32
  13. package/dist/src/modules/Auth/auth.controller.d.ts.map +1 -1
  14. package/dist/src/modules/Auth/auth.controller.js +118 -105
  15. package/dist/src/modules/Rbac/rbac.controller.d.ts.map +1 -1
  16. package/dist/src/modules/Rbac/rbac.controller.js +141 -140
  17. package/dist/src/routes/index.d.ts.map +1 -1
  18. package/dist/src/routes/index.js +0 -5
  19. package/doc/en/CHEATSHEET.md +3 -7
  20. package/doc/en/CLI.md +16 -41
  21. package/doc/en/DEPLOYMENT.md +171 -245
  22. package/doc/en/GETTING_STARTED.md +1 -25
  23. package/doc/en/PACKAGES.md +2 -3
  24. package/doc/en/STRUCTURE.md +1 -11
  25. package/doc/en/TUTORIAL.md +61 -119
  26. package/doc/id/CHANGELOG.md +16 -0
  27. package/doc/id/CHEATSHEET.md +0 -4
  28. package/doc/id/CLI.md +19 -54
  29. package/doc/id/DEPLOYMENT.md +171 -245
  30. package/doc/id/GETTING_STARTED.md +91 -115
  31. package/doc/id/PACKAGES.md +0 -1
  32. package/doc/id/STRUCTURE.md +1 -11
  33. package/doc/id/TUTORIAL.md +51 -109
  34. package/gitignore.template +0 -10
  35. package/lib/bootstrap.ts +39 -38
  36. package/lib/core/store.ts +116 -0
  37. package/lib/middleware/error.ts +1 -21
  38. package/lib/utils/validator.ts +3 -39
  39. package/package.json +4 -18
  40. package/scripts/init-project.js +2 -108
  41. package/scripts/make-module.js +1 -12
  42. package/scripts/seed-json.js +158 -0
  43. package/src/modules/Auth/auth.controller.ts +156 -106
  44. package/src/modules/Rbac/rbac.controller.ts +193 -138
  45. package/src/routes/index.ts +0 -3
  46. package/src/routes/rbac.ts +42 -42
  47. package/storage/logs/.0337f5062fe676994d1dc340156e089444e3d6e0-audit.json +5 -10
  48. package/storage/logs/lapeh-2025-12-30.log +1093 -0
  49. package/tsconfig.build.json +1 -3
  50. package/tsconfig.json +0 -1
  51. package/lib/core/database.ts +0 -5
  52. package/prisma/base.prisma.template +0 -8
  53. package/prisma/migrations/20251225163737_init/migration.sql +0 -236
  54. package/prisma/migrations/20251226000329_create_pets_table/migration.sql +0 -11
  55. package/prisma/migrations/20251226001249_create_pets_table/migration.sql +0 -82
  56. package/prisma/migrations/20251226001717_restore_core_models/migration.sql +0 -236
  57. package/prisma/migrations/migration_lock.toml +0 -3
  58. package/prisma/schema.prisma +0 -197
  59. package/prisma/seed.ts +0 -411
  60. package/scripts/compile-schema.js +0 -64
  61. package/src/modules/Auth/auth.prisma +0 -106
  62. package/src/modules/Pets/pets.controller.ts +0 -238
  63. package/src/modules/Pets/pets.prisma +0 -9
  64. package/src/modules/Rbac/rbac.prisma +0 -68
  65. package/src/routes/pets.ts +0 -13
  66. package/storage/logs/lapeh-2025-12-26.log +0 -88
  67. package/storage/logs/lapeh-2025-12-27.log +0 -217
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lapeh",
3
- "version": "2.6.17",
3
+ "version": "3.0.2",
4
4
  "description": "Framework API Express yang siap pakai (Standardized)",
5
5
  "engines": {
6
6
  "node": ">=18.0.0",
@@ -23,7 +23,6 @@
23
23
  "bin",
24
24
  "dist",
25
25
  "lib",
26
- "prisma",
27
26
  "scripts",
28
27
  "src",
29
28
  "storage",
@@ -42,23 +41,13 @@
42
41
  "scripts": {
43
42
  "dev": "node bin/index.js dev",
44
43
  "first": "node scripts/init-project.js",
45
- "prebuild": "npm run prisma:generate",
46
44
  "build": "node bin/index.js build",
47
- "prestart": "npm run prisma:generate",
48
45
  "start": "node bin/index.js start",
49
- "prestart:prod": "npm run prisma:generate",
50
46
  "start:prod": "node bin/index.js start",
51
47
  "typecheck": "tsc --noEmit",
52
48
  "lint": "eslint .",
53
49
  "lint:fix": "eslint . --fix",
54
50
  "test": "jest",
55
- "prisma:generate": "node scripts/compile-schema.js && prisma generate",
56
- "prisma:migrate": "node scripts/compile-schema.js && prisma migrate dev",
57
- "prisma:deploy": "node scripts/compile-schema.js && prisma migrate deploy",
58
- "db:seed": "prisma db seed",
59
- "db:reset": "prisma migrate reset",
60
- "db:rs": "prisma migrate reset --force && prisma db seed",
61
- "db:studio": "prisma studio",
62
51
  "generate:jwt": "node scripts/generate-jwt-secret.js",
63
52
  "make:module": "node scripts/make-module.js",
64
53
  "make:modul": "node scripts/make-module.js",
@@ -71,7 +60,6 @@
71
60
  "express-framework",
72
61
  "backend-framework",
73
62
  "rest-api",
74
- "prisma-orm",
75
63
  "production-ready",
76
64
  "api-generator",
77
65
  "boilerplate",
@@ -87,9 +75,7 @@
87
75
  "author": "robyajo <robyfull.dev@gmail.com>",
88
76
  "license": "MIT",
89
77
  "type": "commonjs",
90
- "peerDependencies": {
91
- "@prisma/client": "^6.0.0"
92
- },
78
+ "peerDependencies": {},
93
79
  "dependencies": {
94
80
  "@types/bcryptjs": "2.4.6",
95
81
  "@types/compression": "^1.8.1",
@@ -100,7 +86,7 @@
100
86
  "@types/node": "25.0.3",
101
87
  "@types/pg": "8.16.0",
102
88
  "@types/uuid": "10.0.0",
103
- "bcryptjs": "3.0.3",
89
+ "bcryptjs": "^2.4.3",
104
90
  "compression": "^1.8.1",
105
91
  "cors": "2.8.5",
106
92
  "dotenv": "17.2.3",
@@ -126,13 +112,13 @@
126
112
  },
127
113
  "devDependencies": {
128
114
  "@eslint/js": "^9.39.2",
115
+ "@types/bcrypt": "^5.0.2",
129
116
  "@types/jest": "^30.0.0",
130
117
  "@types/module-alias": "^2.0.4",
131
118
  "@types/supertest": "^6.0.3",
132
119
  "eslint": "^9.39.2",
133
120
  "globals": "^16.5.0",
134
121
  "jest": "^30.2.0",
135
- "prisma": "^6.0.0",
136
122
  "supertest": "^7.1.4",
137
123
  "ts-jest": "^29.4.6",
138
124
  "typescript-eslint": "^8.50.1"
@@ -6,7 +6,6 @@ const readline = require("readline");
6
6
  const rootDir = path.join(__dirname, "..");
7
7
  const envExample = path.join(rootDir, ".env.example");
8
8
  const envFile = path.join(rootDir, ".env");
9
- const prismaBaseFile = path.join(rootDir, "prisma", "base.prisma.template");
10
9
 
11
10
  const rl = readline.createInterface({
12
11
  input: process.stdin,
@@ -44,32 +43,6 @@ const selectOption = async (query, options) => {
44
43
  console.log("🚀 Starting project initialization...");
45
44
 
46
45
  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
- { key: "mongo", label: "MongoDB", provider: "mongodb", defaultPort: "27017" },
53
- ]);
54
-
55
- let dbUrl = "";
56
- let dbProvider = dbType.provider;
57
-
58
- const host = await ask("Database Host", "localhost");
59
- const port = await ask("Database Port", dbType.defaultPort);
60
- const user = await ask("Database User", dbType.key === "mongo" ? "" : "root");
61
- const password = await ask("Database Password", "");
62
- const dbName = await ask("Database Name", "lapeh");
63
-
64
- if (dbType.key === "pgsql") {
65
- dbUrl = `postgresql://${user}:${password}@${host}:${port}/${dbName}?schema=public`;
66
- } else if (dbType.key === "mysql") {
67
- dbUrl = `mysql://${user}:${password}@${host}:${port}/${dbName}`;
68
- } else if (dbType.key === "mongo") {
69
- const auth = user ? `${user}:${password}@` : "";
70
- dbUrl = `mongodb://${auth}${host}:${port}/${dbName}?authSource=admin`;
71
- }
72
-
73
46
  // Close readline as we are done with input
74
47
  rl.close();
75
48
 
@@ -80,65 +53,16 @@ const selectOption = async (query, options) => {
80
53
  envContent = fs.readFileSync(envExample, "utf8");
81
54
  } else {
82
55
  // Fallback minimal env if example missing
83
- envContent = `PORT=4000\nDATABASE_PROVIDER="postgresql"\nDATABASE_URL=""\nJWT_SECRET="replace_this"\n`;
84
- }
85
-
86
- // Replace DATABASE_URL and DATABASE_PROVIDER
87
- // Regex to replace existing values or append if missing (simplified)
88
- if (envContent.includes("DATABASE_URL=")) {
89
- envContent = envContent.replace(/DATABASE_URL=".+"/g, `DATABASE_URL="${dbUrl}"`);
90
- envContent = envContent.replace(/DATABASE_URL=.+/g, `DATABASE_URL="${dbUrl}"`); // Handle unquoted
91
- } else {
92
- envContent += `\nDATABASE_URL="${dbUrl}"`;
93
- }
94
-
95
- if (envContent.includes("DATABASE_PROVIDER=")) {
96
- envContent = envContent.replace(/DATABASE_PROVIDER=".+"/g, `DATABASE_PROVIDER="${dbProvider}"`);
97
- envContent = envContent.replace(/DATABASE_PROVIDER=.+/g, `DATABASE_PROVIDER="${dbProvider}"`);
98
- } else {
99
- envContent += `\nDATABASE_PROVIDER="${dbProvider}"`;
56
+ envContent = `PORT=4000\nJWT_SECRET="replace_this"\n`;
100
57
  }
101
58
 
102
59
  fs.writeFileSync(envFile, envContent);
103
- console.log("✅ .env updated with database configuration.");
104
-
105
- // Update base.prisma.template with the correct provider
106
- console.log("\n�️ Updating Prisma configuration...");
107
- if (fs.existsSync(prismaBaseFile)) {
108
- let prismaContent = fs.readFileSync(prismaBaseFile, "utf8");
109
-
110
- // Replace provider in datasource block
111
- // Matches: provider = "..." inside datasource db { ... }
112
- prismaContent = prismaContent.replace(
113
- /(datasource\s+db\s+\{[\s\S]*?provider\s*=\s*")[^"]+(")/,
114
- `$1${dbProvider}$2`
115
- );
116
-
117
- fs.writeFileSync(prismaBaseFile, prismaContent);
118
- console.log(`✅ prisma/base.prisma.template updated to use ${dbProvider}.`);
119
- } else {
120
- console.warn("⚠️ prisma/base.prisma.template not found. Skipping prisma update.");
121
- }
60
+ console.log("✅ .env created.");
122
61
 
123
62
  // 3. Install dependencies
124
63
  console.log("\n📦 Installing dependencies...");
125
64
  execSync("npm install", { stdio: "inherit", cwd: rootDir });
126
65
 
127
- // 4. Create .vscode/settings.json
128
- console.log("\n🛠️ Configuring VS Code...");
129
- const vscodeDir = path.join(rootDir, ".vscode");
130
- if (!fs.existsSync(vscodeDir)) {
131
- fs.mkdirSync(vscodeDir, { recursive: true });
132
- }
133
- const settingsFile = path.join(vscodeDir, "settings.json");
134
- const settingsContent = {
135
- "files.associations": {
136
- "*.model": "prisma"
137
- }
138
- };
139
- fs.writeFileSync(settingsFile, JSON.stringify(settingsContent, null, 2));
140
- console.log("✅ VS Code configured (.model support added).");
141
-
142
66
  // 5. Generate JWT Secret
143
67
  console.log("\n🔑 Generating JWT Secret...");
144
68
  try {
@@ -150,41 +74,11 @@ const selectOption = async (query, options) => {
150
74
  console.warn("⚠️ Failed to generate JWT secret automatically.");
151
75
  }
152
76
 
153
- // 5. Setup Database (Migrate)
154
- console.log("\n🗄️ Setting up database...");
155
- try {
156
- execSync("node scripts/compile-schema.js", { stdio: "inherit", cwd: rootDir });
157
- console.log("⚙️ Generating Prisma Client...");
158
- execSync("npx prisma generate", { stdio: "inherit", cwd: rootDir });
159
-
160
- console.log("🚀 Running Migration...");
161
- if (dbProvider === 'mongodb') {
162
- execSync("npx prisma db push", { stdio: "inherit", cwd: rootDir });
163
- } else {
164
- execSync("npx prisma migrate dev --name init_setup", { stdio: "inherit", cwd: rootDir });
165
- }
166
- } catch (error) {
167
- console.warn(
168
- '⚠️ Database migration had an issue. Please check your database connection in .env and run "npm run prisma:migrate" manually.'
169
- );
170
- }
171
-
172
- // 6. Seed Database
173
- console.log("\n🌱 Seeding database...");
174
- try {
175
- execSync("npm run db:seed", { stdio: "inherit", cwd: rootDir });
176
- } catch (error) {
177
- console.warn(
178
- '⚠️ Database seeding had an issue. You might need to run "npm run db:seed" manually.'
179
- );
180
- }
181
-
182
77
  console.log("\n✅ Setup complete! You can now run:");
183
78
  console.log(" npm run dev");
184
79
 
185
80
  } catch (error) {
186
81
  console.error("\n❌ Setup failed:", error.message);
187
- rl.close();
188
82
  process.exit(1);
189
83
  }
190
84
  })();
@@ -55,8 +55,7 @@ export async function destroy(req: Request, res: Response) {
55
55
  fs.writeFileSync(path.join(moduleDir, `${lowerName}.controller.ts`), controllerContent);
56
56
 
57
57
  // Service (Optional but good for NestJS style)
58
- const serviceContent = `// import { prisma } from "@lapeh/core/database";
59
-
58
+ const serviceContent = `
60
59
  export async function findAll() {
61
60
  return [];
62
61
  }
@@ -83,18 +82,8 @@ export default router;
83
82
  `;
84
83
  fs.writeFileSync(path.join(moduleDir, `${lowerName}.routes.ts`), routeContent);
85
84
 
86
- // Prisma Model
87
- const prismaContent = `model ${name} {
88
- id String @id @default(uuid())
89
- createdAt DateTime @default(now())
90
- updatedAt DateTime @updatedAt
91
- }
92
- `;
93
- fs.writeFileSync(path.join(moduleDir, `${lowerName}.prisma`), prismaContent);
94
-
95
85
  console.log(`✅ Module ${name} created successfully at src/modules/${name}`);
96
86
  console.log(` - ${lowerName}.controller.ts`);
97
87
  console.log(` - ${lowerName}.service.ts`);
98
88
  console.log(` - ${lowerName}.routes.ts`);
99
- console.log(` - ${lowerName}.prisma`);
100
89
  console.log(`\n👉 Don't forget to register the route in src/routes/index.ts!`);
@@ -0,0 +1,158 @@
1
+
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const bcrypt = require('bcryptjs');
5
+ const { v4: uuidv4 } = require('uuid');
6
+
7
+ const DB_PATH = path.join(__dirname, '../database.json');
8
+
9
+ async function seed() {
10
+ console.log('🌱 Seeding database.json...');
11
+
12
+ let store = {
13
+ users: [],
14
+ roles: [],
15
+ permissions: [],
16
+ user_roles: [],
17
+ role_permissions: [],
18
+ user_permissions: []
19
+ };
20
+
21
+ // Load existing if any (optional, but we'll overwrite for seed)
22
+ // if (fs.existsSync(DB_PATH)) {
23
+ // store = JSON.parse(fs.readFileSync(DB_PATH, 'utf-8'));
24
+ // }
25
+
26
+ const passwordHash = await bcrypt.hash('password123', 10);
27
+
28
+ // 1. Seed Users
29
+ const usersData = [
30
+ { name: "Super Admin", email: "sa@sa.com", roleSlug: "super_admin" },
31
+ { name: "Admin User", email: "a@a.com", roleSlug: "admin" },
32
+ { name: "Regular User", email: "u@u.com", roleSlug: "user" }
33
+ ];
34
+
35
+ for (const u of usersData) {
36
+ const user = {
37
+ id: (store.users.length + 1).toString(),
38
+ uuid: uuidv4(),
39
+ name: u.name,
40
+ email: u.email,
41
+ password: passwordHash,
42
+ created_at: new Date(),
43
+ updated_at: new Date(),
44
+ avatar: null,
45
+ avatar_url: null,
46
+ email_verified_at: null,
47
+ remember_token: null
48
+ };
49
+ store.users.push(user);
50
+ console.log(`Created user: ${user.name}`);
51
+ }
52
+
53
+ // 2. Seed Roles
54
+ const rolesData = [
55
+ { name: "Super Admin", slug: "super_admin", description: "Full access to all resources" },
56
+ { name: "Admin", slug: "admin", description: "Administrator" },
57
+ { name: "User", slug: "user", description: "Regular user" }
58
+ ];
59
+
60
+ for (const r of rolesData) {
61
+ const role = {
62
+ id: (store.roles.length + 1).toString(),
63
+ name: r.name,
64
+ slug: r.slug,
65
+ description: r.description,
66
+ created_at: new Date(),
67
+ updated_at: new Date()
68
+ };
69
+ store.roles.push(role);
70
+ console.log(`Created role: ${role.name}`);
71
+ }
72
+
73
+ // 3. Seed Permissions
74
+ const permissionsData = [
75
+ { name: "Manage Users", slug: "users.manage", description: "Create, update, and delete users" },
76
+ { name: "Create Users", slug: "users.create", description: "Create users" },
77
+ { name: "View Users", slug: "users.view", description: "View users" },
78
+ { name: "Update Users", slug: "users.update", description: "Update users" },
79
+ { name: "Delete Users", slug: "users.delete", description: "Delete users" },
80
+ { name: "Manage Roles", slug: "roles.manage", description: "Create, update, and delete roles" },
81
+ { name: "Manage Permissions", slug: "permissions.manage", description: "Create, update, and delete permissions" },
82
+ { name: "Manage Profiles", slug: "profiles.manage", description: "Manage user profiles" }
83
+ ];
84
+
85
+ for (const p of permissionsData) {
86
+ const perm = {
87
+ id: (store.permissions.length + 1).toString(),
88
+ name: p.name,
89
+ slug: p.slug,
90
+ description: p.description,
91
+ created_at: new Date(),
92
+ updated_at: new Date()
93
+ };
94
+ store.permissions.push(perm);
95
+ }
96
+ console.log(`Seeded ${store.permissions.length} permissions`);
97
+
98
+ // 4. Link Users to Roles
99
+ for (const u of usersData) {
100
+ const user = store.users.find(x => x.email === u.email);
101
+ const role = store.roles.find(x => x.slug === u.roleSlug);
102
+ if (user && role) {
103
+ store.user_roles.push({
104
+ id: (store.user_roles.length + 1).toString(),
105
+ user_id: user.id,
106
+ role_id: role.id,
107
+ created_at: new Date()
108
+ });
109
+ }
110
+ }
111
+
112
+ // 5. Link Roles to Permissions
113
+ // Super Admin -> All Permissions
114
+ const superAdminRole = store.roles.find(r => r.slug === 'super_admin');
115
+ if (superAdminRole) {
116
+ for (const p of store.permissions) {
117
+ store.role_permissions.push({
118
+ id: (store.role_permissions.length + 1).toString(),
119
+ role_id: superAdminRole.id,
120
+ permission_id: p.id,
121
+ created_at: new Date()
122
+ });
123
+ }
124
+ }
125
+
126
+ // Admin -> Users.*
127
+ const adminRole = store.roles.find(r => r.slug === 'admin');
128
+ if (adminRole) {
129
+ const adminPerms = store.permissions.filter(p => p.slug.startsWith('users.'));
130
+ for (const p of adminPerms) {
131
+ store.role_permissions.push({
132
+ id: (store.role_permissions.length + 1).toString(),
133
+ role_id: adminRole.id,
134
+ permission_id: p.id,
135
+ created_at: new Date()
136
+ });
137
+ }
138
+ }
139
+
140
+ // User -> profiles.manage
141
+ const userRole = store.roles.find(r => r.slug === 'user');
142
+ if (userRole) {
143
+ const userPerms = store.permissions.filter(p => p.slug === 'profiles.manage');
144
+ for (const p of userPerms) {
145
+ store.role_permissions.push({
146
+ id: (store.role_permissions.length + 1).toString(),
147
+ role_id: userRole.id,
148
+ permission_id: p.id,
149
+ created_at: new Date()
150
+ });
151
+ }
152
+ }
153
+
154
+ fs.writeFileSync(DB_PATH, JSON.stringify(store, null, 2));
155
+ console.log('✅ Database seeded successfully!');
156
+ }
157
+
158
+ seed().catch(console.error);