webspresso 0.0.16 → 0.0.18
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/core/orm/index.js +85 -143
- package/core/orm/model.js +1 -1
- package/package.json +3 -3
- package/plugins/admin-panel/api.js +9 -9
package/core/orm/index.js
CHANGED
|
@@ -7,24 +7,8 @@
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const { createSchemaHelpers, extractColumnsFromSchema, getColumnMeta } = require('./schema-helpers');
|
|
9
9
|
const { defineModel, getModel, getAllModels, hasModel, clearRegistry } = require('./model');
|
|
10
|
-
|
|
11
|
-
// Create zdb instance with zod (zod is a dependency)
|
|
12
|
-
let z;
|
|
13
|
-
try {
|
|
14
|
-
z = require('zod');
|
|
15
|
-
} catch {
|
|
16
|
-
// Zod not installed, zdb will be undefined
|
|
17
|
-
z = null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Export zdb instance directly
|
|
21
|
-
const zdb = z ? createSchemaHelpers(z) : null;
|
|
22
10
|
const { createRepository } = require('./repository');
|
|
23
|
-
const { createQueryBuilder, QueryBuilder } = require('./query-builder');
|
|
24
|
-
const { runTransaction, createTransactionContext } = require('./transaction');
|
|
25
11
|
const { createMigrationManager } = require('./migrations');
|
|
26
|
-
const { scaffoldMigration, scaffoldAlterMigration, scaffoldDropMigration } = require('./migrations/scaffold');
|
|
27
|
-
const { createScopeContext } = require('./scopes');
|
|
28
12
|
const { createSeeder } = require('./seeder');
|
|
29
13
|
|
|
30
14
|
/**
|
|
@@ -52,28 +36,14 @@ function createDatabase(config) {
|
|
|
52
36
|
};
|
|
53
37
|
|
|
54
38
|
const driverName = driverMap[client] || client;
|
|
39
|
+
const projectNodeModules = path.join(process.cwd(), 'node_modules');
|
|
55
40
|
|
|
56
|
-
// Try to
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
for (const resolvePath of resolvePaths) {
|
|
65
|
-
try {
|
|
66
|
-
driverPath = require.resolve(driverName, { paths: [resolvePath] });
|
|
67
|
-
// Pre-load the driver so Knex can find it in Module._cache
|
|
68
|
-
require(driverPath);
|
|
69
|
-
break;
|
|
70
|
-
} catch (e) {
|
|
71
|
-
// Continue to next path
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// If still not found, provide helpful error
|
|
76
|
-
if (!driverPath) {
|
|
41
|
+
// Try to find and pre-load driver from project's node_modules
|
|
42
|
+
try {
|
|
43
|
+
const driverPath = require.resolve(driverName, { paths: [projectNodeModules] });
|
|
44
|
+
require(driverPath); // Pre-load into Module._cache
|
|
45
|
+
} catch (e) {
|
|
46
|
+
// Driver not found in project
|
|
77
47
|
const installCmd = driverName === 'better-sqlite3'
|
|
78
48
|
? 'npm install better-sqlite3 --save'
|
|
79
49
|
: driverName === 'pg'
|
|
@@ -83,24 +53,21 @@ function createDatabase(config) {
|
|
|
83
53
|
: `npm install ${driverName} --save`;
|
|
84
54
|
|
|
85
55
|
throw new Error(
|
|
86
|
-
`Database driver "${driverName}" is not installed in your project
|
|
56
|
+
`Database driver "${driverName}" is not installed in your project.\n` +
|
|
87
57
|
`Please install it with: ${installCmd}\n` +
|
|
88
|
-
`Note: Database drivers are peer dependencies and must be installed in your project's node_modules, not globally.\n` +
|
|
89
58
|
`Current working directory: ${process.cwd()}`
|
|
90
59
|
);
|
|
91
60
|
}
|
|
92
61
|
}
|
|
93
62
|
|
|
94
63
|
// Create Knex instance
|
|
95
|
-
// Knex will try to load the driver from its own node_modules
|
|
96
|
-
// We need to ensure the driver is available in the project's node_modules
|
|
97
64
|
let knexInstance;
|
|
98
65
|
try {
|
|
99
66
|
knexInstance = knex(config);
|
|
100
67
|
} catch (e) {
|
|
101
|
-
//
|
|
102
|
-
if (e.message && (e.message.includes('Cannot find module') || e.message.includes('
|
|
103
|
-
const driverName = config.client;
|
|
68
|
+
// Provide helpful error message
|
|
69
|
+
if (e.message && (e.message.includes('Cannot find module') || e.message.includes('npm install'))) {
|
|
70
|
+
const driverName = driverMap[config.client] || config.client;
|
|
104
71
|
const installCmd = driverName === 'better-sqlite3'
|
|
105
72
|
? 'npm install better-sqlite3 --save'
|
|
106
73
|
: driverName === 'pg'
|
|
@@ -109,34 +76,10 @@ function createDatabase(config) {
|
|
|
109
76
|
? 'npm install mysql2 --save'
|
|
110
77
|
: `npm install ${driverName} --save`;
|
|
111
78
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
driverExists = true;
|
|
117
|
-
} catch (resolveError) {
|
|
118
|
-
// Driver not found in project
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (!driverExists) {
|
|
122
|
-
throw new Error(
|
|
123
|
-
`Database driver "${driverName}" is not installed in your project. ` +
|
|
124
|
-
`Please install it with: ${installCmd}\n` +
|
|
125
|
-
`Note: Database drivers are peer dependencies and must be installed in your project's node_modules, not globally.\n` +
|
|
126
|
-
`Current working directory: ${process.cwd()}\n` +
|
|
127
|
-
`Make sure you run "${installCmd}" in your project directory.`
|
|
128
|
-
);
|
|
129
|
-
} else {
|
|
130
|
-
// Driver exists but Knex can't find it - this is a module resolution issue
|
|
131
|
-
throw new Error(
|
|
132
|
-
`Database driver "${driverName}" is installed but Knex cannot find it. ` +
|
|
133
|
-
`This might be a module resolution issue. Try:\n` +
|
|
134
|
-
`1. Delete node_modules and package-lock.json\n` +
|
|
135
|
-
`2. Run "npm install" again\n` +
|
|
136
|
-
`3. Make sure "${driverName}" is in your package.json dependencies\n` +
|
|
137
|
-
`Original error: ${e.message}`
|
|
138
|
-
);
|
|
139
|
-
}
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Failed to initialize database: ${e.message}\n` +
|
|
81
|
+
`Make sure "${driverName}" is installed: ${installCmd}`
|
|
82
|
+
);
|
|
140
83
|
}
|
|
141
84
|
throw e;
|
|
142
85
|
}
|
|
@@ -146,114 +89,113 @@ function createDatabase(config) {
|
|
|
146
89
|
const migrate = createMigrationManager(knexInstance, migrationConfig);
|
|
147
90
|
|
|
148
91
|
// Default scope context
|
|
149
|
-
|
|
92
|
+
const defaultScopeContext = {
|
|
93
|
+
tenantId: null,
|
|
94
|
+
userId: null,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Model registry
|
|
98
|
+
const models = new Map();
|
|
150
99
|
|
|
151
100
|
/**
|
|
152
|
-
*
|
|
153
|
-
* @param {
|
|
154
|
-
* @returns {
|
|
101
|
+
* Get a model by name
|
|
102
|
+
* @param {string} name - Model name
|
|
103
|
+
* @returns {import('./types').ModelDefinition}
|
|
155
104
|
*/
|
|
156
|
-
function
|
|
157
|
-
|
|
158
|
-
|
|
105
|
+
function getModelInstance(name) {
|
|
106
|
+
const model = models.get(name);
|
|
107
|
+
if (!model) {
|
|
108
|
+
throw new Error(`Model "${name}" is not defined. Make sure you've called defineModel() first.`);
|
|
109
|
+
}
|
|
110
|
+
return model;
|
|
159
111
|
}
|
|
160
112
|
|
|
161
113
|
/**
|
|
162
|
-
*
|
|
163
|
-
* @param {
|
|
164
|
-
* @returns {
|
|
114
|
+
* Check if a model exists
|
|
115
|
+
* @param {string} name - Model name
|
|
116
|
+
* @returns {boolean}
|
|
165
117
|
*/
|
|
166
|
-
function
|
|
167
|
-
return
|
|
118
|
+
function hasModelInstance(name) {
|
|
119
|
+
return models.has(name);
|
|
168
120
|
}
|
|
169
121
|
|
|
170
122
|
/**
|
|
171
|
-
*
|
|
172
|
-
* @
|
|
173
|
-
* @returns {Promise<*>}
|
|
123
|
+
* Get all registered models
|
|
124
|
+
* @returns {Array<import('./types').ModelDefinition>}
|
|
174
125
|
*/
|
|
175
|
-
function
|
|
176
|
-
return
|
|
126
|
+
function getAllModelInstances() {
|
|
127
|
+
return Array.from(models.values());
|
|
177
128
|
}
|
|
178
129
|
|
|
179
130
|
/**
|
|
180
|
-
*
|
|
181
|
-
* @
|
|
131
|
+
* Register a model
|
|
132
|
+
* @param {import('./types').ModelDefinition} model - Model definition
|
|
182
133
|
*/
|
|
183
|
-
function
|
|
184
|
-
|
|
134
|
+
function registerModel(model) {
|
|
135
|
+
models.set(model.name, model);
|
|
185
136
|
}
|
|
186
137
|
|
|
187
138
|
/**
|
|
188
|
-
*
|
|
189
|
-
* @
|
|
139
|
+
* Get repository for a model
|
|
140
|
+
* @param {string} modelName - Model name
|
|
141
|
+
* @param {import('./types').ScopeContext} [scopeContext] - Scope context
|
|
142
|
+
* @returns {import('./types').Repository}
|
|
190
143
|
*/
|
|
191
|
-
|
|
192
|
-
|
|
144
|
+
function getRepository(modelName, scopeContext = defaultScopeContext) {
|
|
145
|
+
const model = getModelInstance(modelName);
|
|
146
|
+
return createRepository(model, knexInstance, scopeContext);
|
|
193
147
|
}
|
|
194
148
|
|
|
195
149
|
/**
|
|
196
|
-
*
|
|
197
|
-
* @param {
|
|
198
|
-
* @
|
|
150
|
+
* Get query builder for a model
|
|
151
|
+
* @param {string} modelName - Model name
|
|
152
|
+
* @param {import('./types').ScopeContext} [scopeContext] - Scope context
|
|
153
|
+
* @returns {import('knex').Knex.QueryBuilder}
|
|
199
154
|
*/
|
|
200
|
-
function
|
|
201
|
-
|
|
155
|
+
function query(modelName, scopeContext = defaultScopeContext) {
|
|
156
|
+
const model = getModelInstance(modelName);
|
|
157
|
+
const repo = createRepository(model, knexInstance, scopeContext);
|
|
158
|
+
return repo.query();
|
|
202
159
|
}
|
|
203
160
|
|
|
204
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Create seeder instance
|
|
163
|
+
* @returns {import('./types').Seeder}
|
|
164
|
+
*/
|
|
165
|
+
function createSeederInstance() {
|
|
166
|
+
return createSeeder(knexInstance, models);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return {
|
|
205
170
|
knex: knexInstance,
|
|
206
|
-
createRepository: createRepo,
|
|
207
|
-
transaction,
|
|
208
171
|
migrate,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
172
|
+
getModel: getModelInstance,
|
|
173
|
+
hasModel: hasModelInstance,
|
|
174
|
+
getAllModels: getAllModelInstances,
|
|
175
|
+
registerModel,
|
|
176
|
+
getRepository,
|
|
177
|
+
query,
|
|
178
|
+
createSeeder: createSeederInstance,
|
|
179
|
+
destroy: () => knexInstance.destroy(),
|
|
213
180
|
};
|
|
214
|
-
|
|
215
|
-
return db;
|
|
216
181
|
}
|
|
217
182
|
|
|
218
|
-
// Export
|
|
183
|
+
// Export zdb instance directly
|
|
184
|
+
const z = require('zod');
|
|
185
|
+
const zdb = z ? createSchemaHelpers(z) : null;
|
|
186
|
+
|
|
219
187
|
module.exports = {
|
|
220
188
|
// Main factory
|
|
221
189
|
createDatabase,
|
|
222
|
-
|
|
223
|
-
// Schema helpers - zdb instance (direct export)
|
|
190
|
+
// Schema helpers
|
|
224
191
|
zdb,
|
|
225
|
-
|
|
226
|
-
extractColumnsFromSchema,
|
|
227
|
-
getColumnMeta,
|
|
228
|
-
|
|
229
|
-
// Model
|
|
192
|
+
// Model utilities
|
|
230
193
|
defineModel,
|
|
231
194
|
getModel,
|
|
232
195
|
getAllModels,
|
|
233
196
|
hasModel,
|
|
234
197
|
clearRegistry,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// Query builder
|
|
240
|
-
createQueryBuilder,
|
|
241
|
-
QueryBuilder,
|
|
242
|
-
|
|
243
|
-
// Transaction
|
|
244
|
-
runTransaction,
|
|
245
|
-
createTransactionContext,
|
|
246
|
-
|
|
247
|
-
// Migrations
|
|
248
|
-
createMigrationManager,
|
|
249
|
-
scaffoldMigration,
|
|
250
|
-
scaffoldAlterMigration,
|
|
251
|
-
scaffoldDropMigration,
|
|
252
|
-
|
|
253
|
-
// Scopes
|
|
254
|
-
createScopeContext,
|
|
255
|
-
|
|
256
|
-
// Seeder
|
|
257
|
-
createSeeder,
|
|
198
|
+
// Column utilities
|
|
199
|
+
extractColumnsFromSchema,
|
|
200
|
+
getColumnMeta,
|
|
258
201
|
};
|
|
259
|
-
|
package/core/orm/model.js
CHANGED
|
@@ -80,7 +80,7 @@ function defineModel(options) {
|
|
|
80
80
|
},
|
|
81
81
|
columns,
|
|
82
82
|
admin: {
|
|
83
|
-
enabled: admin.enabled
|
|
83
|
+
enabled: admin.enabled === true, // Explicit boolean check
|
|
84
84
|
label: admin.label || name,
|
|
85
85
|
icon: admin.icon || null,
|
|
86
86
|
customFields: admin.customFields || {},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webspresso",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "Minimal, production-ready SSR framework for Node.js with file-based routing, Nunjucks templating, built-in i18n, and CLI tooling",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"@faker-js/faker": "^9.0.0",
|
|
53
|
-
"better-sqlite3": "^
|
|
53
|
+
"better-sqlite3": "^11.10.0",
|
|
54
54
|
"dotenv": "^16.0.0",
|
|
55
55
|
"mysql2": "^3.0.0",
|
|
56
56
|
"pg": "^8.0.0"
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@faker-js/faker": "^9.3.0",
|
|
77
77
|
"@vitest/coverage-v8": "^1.2.0",
|
|
78
|
-
"better-sqlite3": "^11.
|
|
78
|
+
"better-sqlite3": "^11.10.0",
|
|
79
79
|
"chokidar": "^3.5.3",
|
|
80
80
|
"dotenv": "^16.3.1",
|
|
81
81
|
"release-it": "^17.11.0",
|
|
@@ -131,7 +131,7 @@ function createApiHandlers(options) {
|
|
|
131
131
|
const adminModels = [];
|
|
132
132
|
|
|
133
133
|
for (const [name, model] of allModels) {
|
|
134
|
-
if (model.admin && model.admin.enabled) {
|
|
134
|
+
if (model.admin && model.admin.enabled === true) {
|
|
135
135
|
adminModels.push({
|
|
136
136
|
name: model.name,
|
|
137
137
|
table: model.table,
|
|
@@ -162,7 +162,7 @@ function createApiHandlers(options) {
|
|
|
162
162
|
return res.status(404).json({ error: 'Model not found' });
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
if (!model.admin ||
|
|
165
|
+
if (!model.admin || model.admin.enabled !== true) {
|
|
166
166
|
return res.status(403).json({ error: 'Model not enabled in admin panel' });
|
|
167
167
|
}
|
|
168
168
|
|
|
@@ -204,7 +204,7 @@ function createApiHandlers(options) {
|
|
|
204
204
|
const { model: modelName } = req.params;
|
|
205
205
|
const model = getModel(modelName);
|
|
206
206
|
|
|
207
|
-
if (!model || !model.admin ||
|
|
207
|
+
if (!model || !model.admin || model.admin.enabled !== true) {
|
|
208
208
|
return res.status(404).json({ error: 'Model not found or not enabled' });
|
|
209
209
|
}
|
|
210
210
|
|
|
@@ -270,7 +270,7 @@ function createApiHandlers(options) {
|
|
|
270
270
|
const { model: modelName, id } = req.params;
|
|
271
271
|
const model = getModel(modelName);
|
|
272
272
|
|
|
273
|
-
if (!model || !model.admin ||
|
|
273
|
+
if (!model || !model.admin || model.admin.enabled !== true) {
|
|
274
274
|
return res.status(404).json({ error: 'Model not found or not enabled' });
|
|
275
275
|
}
|
|
276
276
|
|
|
@@ -295,7 +295,7 @@ function createApiHandlers(options) {
|
|
|
295
295
|
const { model: modelName } = req.params;
|
|
296
296
|
const model = getModel(modelName);
|
|
297
297
|
|
|
298
|
-
if (!model || !model.admin ||
|
|
298
|
+
if (!model || !model.admin || model.admin.enabled !== true) {
|
|
299
299
|
return res.status(404).json({ error: 'Model not found or not enabled' });
|
|
300
300
|
}
|
|
301
301
|
|
|
@@ -316,7 +316,7 @@ function createApiHandlers(options) {
|
|
|
316
316
|
const { model: modelName, id } = req.params;
|
|
317
317
|
const model = getModel(modelName);
|
|
318
318
|
|
|
319
|
-
if (!model || !model.admin ||
|
|
319
|
+
if (!model || !model.admin || model.admin.enabled !== true) {
|
|
320
320
|
return res.status(404).json({ error: 'Model not found or not enabled' });
|
|
321
321
|
}
|
|
322
322
|
|
|
@@ -341,7 +341,7 @@ function createApiHandlers(options) {
|
|
|
341
341
|
const { model: modelName, id } = req.params;
|
|
342
342
|
const model = getModel(modelName);
|
|
343
343
|
|
|
344
|
-
if (!model || !model.admin ||
|
|
344
|
+
if (!model || !model.admin || model.admin.enabled !== true) {
|
|
345
345
|
return res.status(404).json({ error: 'Model not found or not enabled' });
|
|
346
346
|
}
|
|
347
347
|
|
|
@@ -366,7 +366,7 @@ function createApiHandlers(options) {
|
|
|
366
366
|
const { model: modelName, relation: relationName } = req.params;
|
|
367
367
|
const model = getModel(modelName);
|
|
368
368
|
|
|
369
|
-
if (!model || !model.admin ||
|
|
369
|
+
if (!model || !model.admin || model.admin.enabled !== true) {
|
|
370
370
|
return res.status(404).json({ error: 'Model not found or not enabled' });
|
|
371
371
|
}
|
|
372
372
|
|
|
@@ -395,7 +395,7 @@ function createApiHandlers(options) {
|
|
|
395
395
|
const { model: modelName, query: queryName } = req.params;
|
|
396
396
|
const model = getModel(modelName);
|
|
397
397
|
|
|
398
|
-
if (!model || !model.admin ||
|
|
398
|
+
if (!model || !model.admin || model.admin.enabled !== true) {
|
|
399
399
|
return res.status(404).json({ error: 'Model not found or not enabled' });
|
|
400
400
|
}
|
|
401
401
|
|