webspresso 0.0.90 → 0.0.93
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 +98 -2509
- package/bin/commands/admin-password.js +8 -2
- package/bin/commands/api.js +7 -1
- package/bin/commands/favicon-generate.js +6 -4
- package/bin/commands/mcp.js +162 -0
- package/bin/commands/new.js +7 -1
- package/bin/commands/page.js +7 -1
- package/bin/commands/seed.js +7 -1
- package/bin/commands/service.js +199 -0
- package/bin/commands/skill.js +7 -1
- package/bin/webspresso.js +4 -0
- package/core/auth/middleware.js +9 -0
- package/core/compileSchema.js +1 -5
- package/core/compression/compressible.js +63 -0
- package/core/compression/compression-stream.js +59 -0
- package/core/compression/index.js +25 -0
- package/core/compression/middleware.js +310 -0
- package/core/compression/negotiate.js +165 -0
- package/core/errors/base.js +31 -0
- package/core/errors/domain.js +193 -0
- package/core/errors/http.js +133 -0
- package/core/errors/index.js +77 -0
- package/core/errors/middleware.js +162 -0
- package/core/errors/normalize.js +171 -0
- package/core/kernel/app.js +2 -1
- package/core/kernel/index.js +5 -1
- package/core/orm/cache/fingerprint.js +23 -5
- package/core/orm/complexity.js +120 -0
- package/core/orm/eager-loader.js +54 -9
- package/core/orm/events.js +64 -14
- package/core/orm/index.js +83 -12
- package/core/orm/model.js +13 -2
- package/core/orm/query-builder.js +30 -4
- package/core/orm/repository.js +57 -25
- package/core/orm/transaction.js +39 -5
- package/core/orm/utils.js +6 -3
- package/core/queue/adapters/database.js +317 -0
- package/core/queue/adapters/memory.js +176 -0
- package/core/queue/adapters/redis.js +357 -0
- package/core/queue/index.js +21 -0
- package/core/queue/job.js +103 -0
- package/core/queue/manager.js +413 -0
- package/core/realtime/adapters/redis.js +431 -0
- package/core/realtime/adapters/socket-io.js +203 -0
- package/core/realtime/adapters/sse.js +210 -0
- package/core/realtime/adapters/websocket.js +337 -0
- package/core/realtime/auth-manager.js +140 -0
- package/core/realtime/core.js +359 -0
- package/core/realtime/emitter.js +98 -0
- package/core/realtime/errors.js +71 -0
- package/core/realtime/index.js +79 -0
- package/core/realtime/reconnect-manager.js +118 -0
- package/core/realtime/registry.js +243 -0
- package/core/shutdown/http-adapter.js +134 -0
- package/core/shutdown/index.js +12 -0
- package/core/shutdown/shutdown-manager.js +312 -0
- package/core/ssr/index.js +13 -0
- package/core/ssr/stream.js +174 -0
- package/core/validation/index.js +70 -0
- package/index.d.ts +924 -53
- package/index.js +110 -1
- package/package.json +30 -2
- package/plugins/admin-panel/api.js +37 -4
- package/plugins/admin-panel/app.js +57 -17
- package/plugins/admin-panel/client/parts/01-state-api-breadcrumb.js +10 -2
- package/plugins/admin-panel/client/parts/04-field-renderers.js +102 -58
- package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +51 -27
- package/plugins/admin-panel/client/parts/07-model-list.js +9 -4
- package/plugins/admin-panel/client/parts/08-record-list.js +37 -26
- package/plugins/admin-panel/client/parts/09-record-form.js +168 -19
- package/plugins/admin-panel/core/admin-module.js +2 -2
- package/plugins/admin-panel/core/api-extensions.js +149 -0
- package/plugins/admin-panel/field-renderers/rich-text.js +3 -2
- package/plugins/admin-panel/index.js +155 -23
- package/plugins/admin-panel/modules/bulk-actions.js +9 -2
- package/plugins/admin-panel/modules/custom-pages.js +144 -45
- package/plugins/admin-panel/modules/dashboard.js +8 -1
- package/plugins/admin-panel/modules/menu.js +85 -0
- package/plugins/admin-panel/modules/user-management.js +5 -2
- package/plugins/admin-panel/vendor/mithril.min.js +1 -0
- package/plugins/admin-panel/vendor/quill.min.js +7 -0
- package/plugins/admin-panel/vendor/quill.snow.min.css +945 -0
- package/plugins/admin-panel/vendor/tailwind.min.js +65 -0
- package/plugins/basic-auth/index.js +293 -0
- package/plugins/data-exchange/export-xlsx.js +28 -2
- package/plugins/data-exchange/import.js +55 -16
- package/plugins/data-exchange/index.js +19 -0
- package/plugins/data-exchange/parse-table.js +14 -5
- package/plugins/data-exchange/record-selection.js +2 -0
- package/plugins/email/index.js +14 -1
- package/plugins/index.js +33 -0
- package/plugins/mcp/auth.js +104 -0
- package/plugins/mcp/discovery.js +88 -0
- package/plugins/mcp/index.js +208 -0
- package/plugins/mcp/prompts.js +149 -0
- package/plugins/mcp/providers/orm.js +325 -0
- package/plugins/mcp/providers/services.js +190 -0
- package/plugins/mcp/providers/system.js +87 -0
- package/plugins/mcp/server.js +472 -0
- package/plugins/mcp/transports/sse.js +139 -0
- package/plugins/mcp/transports/stdio.js +108 -0
- package/plugins/queue/index.js +108 -0
- package/plugins/realtime/index.js +150 -0
- package/plugins/recaptcha/index.js +11 -0
- package/plugins/redirect/index.js +9 -1
- package/plugins/rest-resources/index.js +280 -11
- package/plugins/upload/index.js +16 -0
- package/src/app-context.js +46 -6
- package/src/file-router.js +366 -130
- package/src/helpers.js +20 -2
- package/src/njk-frontmatter.js +24 -14
- package/src/plugin-manager.js +124 -4
- package/src/server.js +332 -54
- package/src/services/builtins/auth.js +273 -0
- package/src/services/builtins/exchange.js +229 -0
- package/src/services/builtins/mail.js +134 -0
- package/src/services/builtins/media.js +196 -0
- package/src/services/builtins/system.js +221 -0
- package/src/services/discovery.js +141 -0
- package/src/services/executor.js +292 -0
- package/src/services/index.js +70 -0
- package/src/services/memoize.js +256 -0
- package/src/services/registry.js +290 -0
- package/src/services/validator.js +230 -0
- package/templates/skills/webspresso-usage/REFERENCE-framework.md +6 -4
package/index.js
CHANGED
|
@@ -10,9 +10,21 @@ const {
|
|
|
10
10
|
getAppContext,
|
|
11
11
|
getDb,
|
|
12
12
|
hasDb,
|
|
13
|
+
getShutdownManager,
|
|
14
|
+
hasShutdownManager,
|
|
15
|
+
getServiceRegistry,
|
|
16
|
+
hasServiceRegistry,
|
|
13
17
|
resetAppContext,
|
|
14
18
|
setAppContext,
|
|
15
19
|
} = require('./src/app-context');
|
|
20
|
+
const {
|
|
21
|
+
createServiceRegistry,
|
|
22
|
+
defineService,
|
|
23
|
+
ServiceRegistry,
|
|
24
|
+
} = require('./src/services');
|
|
25
|
+
const { ShutdownManager, NodeHttpAdapter } = require('./core/shutdown');
|
|
26
|
+
const compression = require('./core/compression');
|
|
27
|
+
const errors = require('./core/errors');
|
|
16
28
|
const {
|
|
17
29
|
mountPages,
|
|
18
30
|
filePathToRoute,
|
|
@@ -43,7 +55,7 @@ const {
|
|
|
43
55
|
// ORM exports (lazy loaded)
|
|
44
56
|
const orm = require('./core/orm');
|
|
45
57
|
|
|
46
|
-
/**
|
|
58
|
+
/** Standalone experimental application kernel (event bus, plugin shell, view resolver, flows). Distinct from framework SSR `createApp`. */
|
|
47
59
|
const kernel = require('./core/kernel');
|
|
48
60
|
|
|
49
61
|
// Built-in plugins
|
|
@@ -64,6 +76,20 @@ const {
|
|
|
64
76
|
rateLimitPlugin,
|
|
65
77
|
contentPlugin,
|
|
66
78
|
csrfPlugin,
|
|
79
|
+
corsPlugin,
|
|
80
|
+
emailPlugin,
|
|
81
|
+
basicAuthPlugin,
|
|
82
|
+
realtimePlugin,
|
|
83
|
+
realtime,
|
|
84
|
+
createRealtime,
|
|
85
|
+
websocket,
|
|
86
|
+
sse,
|
|
87
|
+
socketIo,
|
|
88
|
+
redis,
|
|
89
|
+
createRedisAdapter,
|
|
90
|
+
mcpPlugin,
|
|
91
|
+
createMcpServer,
|
|
92
|
+
McpServer,
|
|
67
93
|
} = require('./plugins');
|
|
68
94
|
|
|
69
95
|
const content = require('./core/content');
|
|
@@ -78,8 +104,67 @@ module.exports = {
|
|
|
78
104
|
getAppContext,
|
|
79
105
|
getDb,
|
|
80
106
|
hasDb,
|
|
107
|
+
getShutdownManager,
|
|
108
|
+
hasShutdownManager,
|
|
109
|
+
getServiceRegistry,
|
|
110
|
+
hasServiceRegistry,
|
|
81
111
|
resetAppContext,
|
|
82
112
|
setAppContext,
|
|
113
|
+
|
|
114
|
+
// Services Layer
|
|
115
|
+
createServiceRegistry,
|
|
116
|
+
defineService,
|
|
117
|
+
service: defineService,
|
|
118
|
+
ServiceRegistry,
|
|
119
|
+
memoize: require('./src/services').memoize,
|
|
120
|
+
parseTtlMs: require('./src/services').parseTtlMs,
|
|
121
|
+
stableCacheKey: require('./src/services').stableCacheKey,
|
|
122
|
+
sanitizeInput: require('./src/services').sanitizeInput,
|
|
123
|
+
maskSensitiveData: require('./src/services').maskSensitiveData,
|
|
124
|
+
discoverServices: require('./src/services').discoverServices,
|
|
125
|
+
filePathToServiceName: require('./src/services').filePathToServiceName,
|
|
126
|
+
toCamelCase: require('./src/services').toCamelCase,
|
|
127
|
+
createAuthServices: require('./src/services').createAuthServices,
|
|
128
|
+
createMailServices: require('./src/services').createMailServices,
|
|
129
|
+
createMediaServices: require('./src/services').createMediaServices,
|
|
130
|
+
createSystemServices: require('./src/services').createSystemServices,
|
|
131
|
+
createExchangeServices: require('./src/services').createExchangeServices,
|
|
132
|
+
validateServiceInput: require('./src/services').validateServiceInput,
|
|
133
|
+
executeService: require('./src/services').executeService,
|
|
134
|
+
|
|
135
|
+
// Shutdown & Lifecycle
|
|
136
|
+
ShutdownManager,
|
|
137
|
+
NodeHttpAdapter,
|
|
138
|
+
|
|
139
|
+
// HTTP Response Compression
|
|
140
|
+
compression,
|
|
141
|
+
createCompressionMiddleware: compression.createCompressionMiddleware,
|
|
142
|
+
|
|
143
|
+
// Exceptions & Error Handling
|
|
144
|
+
errors,
|
|
145
|
+
WebspressoError: errors.WebspressoError,
|
|
146
|
+
HttpError: errors.HttpError,
|
|
147
|
+
BadRequestError: errors.BadRequestError,
|
|
148
|
+
UnauthorizedError: errors.UnauthorizedError,
|
|
149
|
+
ForbiddenError: errors.ForbiddenError,
|
|
150
|
+
NotFoundError: errors.NotFoundError,
|
|
151
|
+
MethodNotAllowedError: errors.MethodNotAllowedError,
|
|
152
|
+
ConflictError: errors.ConflictError,
|
|
153
|
+
PayloadTooLargeError: errors.PayloadTooLargeError,
|
|
154
|
+
UnsupportedMediaTypeError: errors.UnsupportedMediaTypeError,
|
|
155
|
+
UnprocessableEntityError: errors.UnprocessableEntityError,
|
|
156
|
+
TooManyRequestsError: errors.TooManyRequestsError,
|
|
157
|
+
ValidationError: errors.ValidationError,
|
|
158
|
+
ConfigurationError: errors.ConfigurationError,
|
|
159
|
+
PluginError: errors.PluginError,
|
|
160
|
+
SecurityError: errors.SecurityError,
|
|
161
|
+
RequestError: errors.RequestError,
|
|
162
|
+
RequestAbortedError: errors.RequestAbortedError,
|
|
163
|
+
RouterError: errors.RouterError,
|
|
164
|
+
RouteNotFoundError: errors.RouteNotFoundError,
|
|
165
|
+
RouteGenerationError: errors.RouteGenerationError,
|
|
166
|
+
normalizeError: errors.normalizeError,
|
|
167
|
+
toErrorResponseObject: errors.toErrorResponseObject,
|
|
83
168
|
|
|
84
169
|
// Router utilities (for advanced use)
|
|
85
170
|
mountPages,
|
|
@@ -97,6 +182,11 @@ module.exports = {
|
|
|
97
182
|
// Template helpers
|
|
98
183
|
createHelpers,
|
|
99
184
|
utils,
|
|
185
|
+
|
|
186
|
+
// SSR Streaming
|
|
187
|
+
ssr: require('./core/ssr'),
|
|
188
|
+
renderStream: require('./core/ssr').renderStream,
|
|
189
|
+
createHtmlStream: require('./core/ssr').createHtmlStream,
|
|
100
190
|
|
|
101
191
|
// Asset management
|
|
102
192
|
AssetManager,
|
|
@@ -135,7 +225,26 @@ module.exports = {
|
|
|
135
225
|
rateLimitPlugin,
|
|
136
226
|
contentPlugin,
|
|
137
227
|
csrfPlugin,
|
|
228
|
+
corsPlugin,
|
|
229
|
+
emailPlugin,
|
|
230
|
+
basicAuthPlugin,
|
|
231
|
+
realtimePlugin,
|
|
232
|
+
realtime,
|
|
233
|
+
createRealtime,
|
|
234
|
+
websocket,
|
|
235
|
+
sse,
|
|
236
|
+
socketIo,
|
|
237
|
+
redis,
|
|
238
|
+
createRedisAdapter,
|
|
239
|
+
queuePlugin: require('./plugins/queue'),
|
|
240
|
+
queue: require('./core/queue'),
|
|
241
|
+
createQueueManager: require('./core/queue').createQueueManager,
|
|
242
|
+
QueueManager: require('./core/queue').QueueManager,
|
|
243
|
+
mcpPlugin,
|
|
244
|
+
createMcpServer,
|
|
245
|
+
McpServer,
|
|
138
246
|
|
|
139
247
|
// Schema-driven CMS core (framework-agnostic)
|
|
140
248
|
content,
|
|
141
249
|
};
|
|
250
|
+
|
package/package.json
CHANGED
|
@@ -1,14 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webspresso",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.93",
|
|
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
|
"types": "index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./index.d.ts",
|
|
10
|
+
"default": "./index.js"
|
|
11
|
+
},
|
|
12
|
+
"./errors": {
|
|
13
|
+
"types": "./index.d.ts",
|
|
14
|
+
"default": "./core/errors/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./plugins": "./plugins/index.js",
|
|
17
|
+
"./plugins/*": "./plugins/*",
|
|
18
|
+
"./realtime": "./core/realtime/index.js",
|
|
19
|
+
"./core/*": "./core/*",
|
|
20
|
+
"./src/*": "./src/*",
|
|
21
|
+
"./views/*": "./views/*",
|
|
22
|
+
"./templates/*": "./templates/*",
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
7
25
|
"bin": {
|
|
8
26
|
"webspresso": "./bin/webspresso.js"
|
|
9
27
|
},
|
|
10
28
|
"scripts": {
|
|
11
29
|
"test": "vitest run",
|
|
30
|
+
"test:security": "vitest run tests/security/",
|
|
31
|
+
"security": "npm run test:security && npm audit",
|
|
12
32
|
"test:watch": "vitest",
|
|
13
33
|
"test:coverage": "vitest run --coverage",
|
|
14
34
|
"test:e2e": "playwright test",
|
|
@@ -22,6 +42,10 @@
|
|
|
22
42
|
"bench:ci:local": "npm run bench:baseline && npm run bench:ci",
|
|
23
43
|
"check:types": "tsc --project tests/ts-smoke/tsconfig.json",
|
|
24
44
|
"rebuild:native": "npm rebuild better-sqlite3 bcrypt sharp",
|
|
45
|
+
"docs:dev": "docboot dev ./docs",
|
|
46
|
+
"docs:build": "docboot build ./docs",
|
|
47
|
+
"docs:serve": "docboot serve dist",
|
|
48
|
+
"docs:doctor": "docboot doctor ./docs",
|
|
25
49
|
"release": "release-it"
|
|
26
50
|
},
|
|
27
51
|
"keywords": [
|
|
@@ -62,7 +86,7 @@
|
|
|
62
86
|
"csv-parse": "^5.6.0",
|
|
63
87
|
"dayjs": "^1.11.19",
|
|
64
88
|
"exceljs": "^4.4.0",
|
|
65
|
-
"express": "^
|
|
89
|
+
"express": "^5.2.1",
|
|
66
90
|
"express-session": "^1.18.0",
|
|
67
91
|
"helmet": "^7.2.0",
|
|
68
92
|
"inquirer": "^8.2.6",
|
|
@@ -114,6 +138,7 @@
|
|
|
114
138
|
"@vitest/coverage-v8": "^3.0.0",
|
|
115
139
|
"better-sqlite3": "^11.10.0",
|
|
116
140
|
"chokidar": "^3.5.3",
|
|
141
|
+
"docboot": "^0.3.0",
|
|
117
142
|
"dotenv": "^16.3.1",
|
|
118
143
|
"express-rate-limit": "^8.4.1",
|
|
119
144
|
"release-it": "^20.2.1",
|
|
@@ -129,5 +154,8 @@
|
|
|
129
154
|
"tar": "^7.5.11",
|
|
130
155
|
"undici": "^6.24.0",
|
|
131
156
|
"uuid": "^11.1.1"
|
|
157
|
+
},
|
|
158
|
+
"allowScripts": {
|
|
159
|
+
"better-sqlite3@11.10.0": true
|
|
132
160
|
}
|
|
133
161
|
}
|
|
@@ -384,6 +384,9 @@ function isColumnSortable(name, meta, model) {
|
|
|
384
384
|
|
|
385
385
|
// Apply filters
|
|
386
386
|
for (const [colName, filter] of Object.entries(filterParams)) {
|
|
387
|
+
if (!model.columns.has(colName) || !filter || typeof filter !== 'object') {
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
387
390
|
const colMeta = model.columns.get(colName);
|
|
388
391
|
const colType = colMeta?.type || 'string';
|
|
389
392
|
const op = filter.op || (colType === 'boolean' ? 'eq' : 'contains');
|
|
@@ -568,6 +571,32 @@ function isColumnSortable(name, meta, model) {
|
|
|
568
571
|
}
|
|
569
572
|
}
|
|
570
573
|
|
|
574
|
+
/** Extract structured field errors from Zod, ORM or database validation exceptions */
|
|
575
|
+
function extractValidationErrors(error) {
|
|
576
|
+
if (!error) return null;
|
|
577
|
+
const fields = {};
|
|
578
|
+
if (error.errors && typeof error.errors === 'object') {
|
|
579
|
+
if (Array.isArray(error.errors)) {
|
|
580
|
+
for (const item of error.errors) {
|
|
581
|
+
if (item.path && item.message) {
|
|
582
|
+
const key = Array.isArray(item.path) ? item.path.join('.') : String(item.path);
|
|
583
|
+
fields[key] = item.message;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
} else {
|
|
587
|
+
Object.assign(fields, error.errors);
|
|
588
|
+
}
|
|
589
|
+
} else if (error.issues && Array.isArray(error.issues)) {
|
|
590
|
+
for (const issue of error.issues) {
|
|
591
|
+
const key = issue.path && issue.path.length > 0 ? issue.path.join('.') : 'root';
|
|
592
|
+
fields[key] = issue.message;
|
|
593
|
+
}
|
|
594
|
+
} else if (error.fields && typeof error.fields === 'object') {
|
|
595
|
+
Object.assign(fields, error.fields);
|
|
596
|
+
}
|
|
597
|
+
return Object.keys(fields).length > 0 ? fields : null;
|
|
598
|
+
}
|
|
599
|
+
|
|
571
600
|
/**
|
|
572
601
|
* Create record
|
|
573
602
|
*/
|
|
@@ -588,7 +617,8 @@ function isColumnSortable(name, meta, model) {
|
|
|
588
617
|
const value = req.body[colName];
|
|
589
618
|
if (isRichTextEmpty(value)) {
|
|
590
619
|
return res.status(400).json({
|
|
591
|
-
error: `Field "${colName}" is required
|
|
620
|
+
error: `Field "${colName}" is required`,
|
|
621
|
+
fields: { [colName]: `Field "${colName}" is required` },
|
|
592
622
|
});
|
|
593
623
|
}
|
|
594
624
|
}
|
|
@@ -599,7 +629,8 @@ function isColumnSortable(name, meta, model) {
|
|
|
599
629
|
|
|
600
630
|
res.status(201).json({ data: sanitizeForOutput(record, model) });
|
|
601
631
|
} catch (error) {
|
|
602
|
-
|
|
632
|
+
const fields = extractValidationErrors(error);
|
|
633
|
+
res.status(400).json({ error: error.message, ...(fields ? { fields } : {}) });
|
|
603
634
|
}
|
|
604
635
|
}
|
|
605
636
|
|
|
@@ -623,7 +654,8 @@ function isColumnSortable(name, meta, model) {
|
|
|
623
654
|
const value = req.body[colName];
|
|
624
655
|
if (isRichTextEmpty(value)) {
|
|
625
656
|
return res.status(400).json({
|
|
626
|
-
error: `Field "${colName}" is required
|
|
657
|
+
error: `Field "${colName}" is required`,
|
|
658
|
+
fields: { [colName]: `Field "${colName}" is required` },
|
|
627
659
|
});
|
|
628
660
|
}
|
|
629
661
|
}
|
|
@@ -638,7 +670,8 @@ function isColumnSortable(name, meta, model) {
|
|
|
638
670
|
|
|
639
671
|
res.json({ data: sanitizeForOutput(record, model) });
|
|
640
672
|
} catch (error) {
|
|
641
|
-
|
|
673
|
+
const fields = extractValidationErrors(error);
|
|
674
|
+
res.status(400).json({ error: error.message, ...(fields ? { fields } : {}) });
|
|
642
675
|
}
|
|
643
676
|
}
|
|
644
677
|
|
|
@@ -247,36 +247,76 @@ function wrapCustomPageWithLayout(page, rawComp) {
|
|
|
247
247
|
|
|
248
248
|
return {
|
|
249
249
|
oninit: function(vnode) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
250
|
+
try {
|
|
251
|
+
vnode.state.instance = typeof rawComp === 'function' ? rawComp.call(this, vnode) : rawComp;
|
|
252
|
+
if (vnode.state.instance && typeof vnode.state.instance.oninit === 'function') {
|
|
253
|
+
return vnode.state.instance.oninit.call(this, vnode);
|
|
254
|
+
}
|
|
255
|
+
} catch (err) {
|
|
256
|
+
console.error('[Admin Panel] Error in oninit for custom page "' + page.id + '":', err);
|
|
257
|
+
vnode.state.renderError = err;
|
|
253
258
|
}
|
|
254
259
|
},
|
|
255
260
|
oncreate: function(vnode) {
|
|
256
|
-
|
|
257
|
-
|
|
261
|
+
try {
|
|
262
|
+
const comp = vnode.state.instance || rawComp;
|
|
263
|
+
if (comp && typeof comp.oncreate === 'function') return comp.oncreate.call(this, vnode);
|
|
264
|
+
} catch (err) {
|
|
265
|
+
console.error('[Admin Panel] Error in oncreate for custom page "' + page.id + '":', err);
|
|
266
|
+
}
|
|
258
267
|
},
|
|
259
268
|
onupdate: function(vnode) {
|
|
260
|
-
|
|
261
|
-
|
|
269
|
+
try {
|
|
270
|
+
const comp = vnode.state.instance || rawComp;
|
|
271
|
+
if (comp && typeof comp.onupdate === 'function') return comp.onupdate.call(this, vnode);
|
|
272
|
+
} catch (err) {
|
|
273
|
+
console.error('[Admin Panel] Error in onupdate for custom page "' + page.id + '":', err);
|
|
274
|
+
}
|
|
262
275
|
},
|
|
263
276
|
onbeforeremove: function(vnode) {
|
|
264
|
-
|
|
265
|
-
|
|
277
|
+
try {
|
|
278
|
+
const comp = vnode.state.instance || rawComp;
|
|
279
|
+
if (comp && typeof comp.onbeforeremove === 'function') return comp.onbeforeremove.call(this, vnode);
|
|
280
|
+
} catch (err) {
|
|
281
|
+
console.error('[Admin Panel] Error in onbeforeremove for custom page "' + page.id + '":', err);
|
|
282
|
+
}
|
|
266
283
|
},
|
|
267
284
|
onremove: function(vnode) {
|
|
268
|
-
|
|
269
|
-
|
|
285
|
+
try {
|
|
286
|
+
const comp = vnode.state.instance || rawComp;
|
|
287
|
+
if (comp && typeof comp.onremove === 'function') return comp.onremove.call(this, vnode);
|
|
288
|
+
} catch (err) {
|
|
289
|
+
console.error('[Admin Panel] Error in onremove for custom page "' + page.id + '":', err);
|
|
290
|
+
}
|
|
270
291
|
},
|
|
271
292
|
view: function(vnode) {
|
|
293
|
+
if (vnode.state.renderError) {
|
|
294
|
+
var err = vnode.state.renderError;
|
|
295
|
+
var breadcrumbs = [{ label: page.title, href: page.path }];
|
|
296
|
+
return m(Layout, [
|
|
297
|
+
m(Breadcrumb, { items: breadcrumbs }),
|
|
298
|
+
m('.p-6.bg-red-50.border.border-red-200.rounded-xl', [
|
|
299
|
+
m('h3.text-lg.font-semibold.text-red-800', 'Sayfa Yüklenirken Hata Oluştu'),
|
|
300
|
+
m('p.text-sm.text-red-600.mt-1', err.message || String(err)),
|
|
301
|
+
])
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
272
304
|
const comp = vnode.state.instance || rawComp;
|
|
273
305
|
var content;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
306
|
+
try {
|
|
307
|
+
if (comp && typeof comp.view === 'function') {
|
|
308
|
+
content = comp.view.call(this, vnode);
|
|
309
|
+
} else if (typeof comp === 'function') {
|
|
310
|
+
content = comp.call(this, vnode);
|
|
311
|
+
} else {
|
|
312
|
+
content = comp;
|
|
313
|
+
}
|
|
314
|
+
} catch (err) {
|
|
315
|
+
console.error('[Admin Panel] Error rendering custom page "' + page.id + '":', err);
|
|
316
|
+
content = m('.p-6.bg-red-50.border.border-red-200.rounded-xl', [
|
|
317
|
+
m('h3.text-lg.font-semibold.text-red-800', 'Sayfa Çizilirken Hata Oluştu'),
|
|
318
|
+
m('p.text-sm.text-red-600.mt-1', err.message || String(err)),
|
|
319
|
+
]);
|
|
280
320
|
}
|
|
281
321
|
if (content && (content.tag === Layout || (content.tag && content.tag.name === 'Layout'))) {
|
|
282
322
|
return content;
|
|
@@ -18,7 +18,10 @@ const api = {
|
|
|
18
18
|
if (response.status === 401 && path.indexOf('/auth/') !== 0) {
|
|
19
19
|
redirectToLogin();
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
const err = new Error(error.error || 'Request failed');
|
|
22
|
+
if (error.fields) err.fields = error.fields;
|
|
23
|
+
err.response = error;
|
|
24
|
+
throw err;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
return response.json();
|
|
@@ -56,10 +59,15 @@ async function downloadDataExchangeXlsx(modelName, payload) {
|
|
|
56
59
|
const blob = await res.blob();
|
|
57
60
|
var url = URL.createObjectURL(blob);
|
|
58
61
|
var a = document.createElement('a');
|
|
62
|
+
a.style.display = 'none';
|
|
59
63
|
a.href = url;
|
|
60
64
|
a.download = modelName + '-export.xlsx';
|
|
65
|
+
document.body.appendChild(a);
|
|
61
66
|
a.click();
|
|
62
|
-
|
|
67
|
+
setTimeout(function () {
|
|
68
|
+
if (a.parentNode) a.parentNode.removeChild(a);
|
|
69
|
+
URL.revokeObjectURL(url);
|
|
70
|
+
}, 1000);
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
// Helper: Capitalize first letter of each word
|