apostrophe 3.17.0 → 3.18.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/.editorconfig +3 -0
- package/.eslintrc +4 -3
- package/.github/workflows/main.yml +2 -2
- package/.stylelintrc +12 -2
- package/CHANGELOG.md +34 -2
- package/defaults.js +2 -2
- package/index.js +124 -33
- package/lib/escape-host.js +8 -0
- package/lib/mongodb-connect.js +55 -0
- package/lib/opentelemetry.js +144 -0
- package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +2 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +20 -8
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +10 -0
- package/modules/@apostrophecms/asset/lib/globalIcons.js +1 -0
- package/modules/@apostrophecms/attachment/index.js +81 -29
- package/modules/@apostrophecms/db/index.js +7 -10
- package/modules/@apostrophecms/doc/index.js +138 -23
- package/modules/@apostrophecms/doc-type/index.js +162 -63
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +39 -1
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +11 -1
- package/modules/@apostrophecms/email/index.js +1 -1
- package/modules/@apostrophecms/express/index.js +2 -2
- package/modules/@apostrophecms/http/index.js +2 -1
- package/modules/@apostrophecms/i18n/i18n/en.json +10 -0
- package/modules/@apostrophecms/i18n/i18n/es.json +7 -0
- package/modules/@apostrophecms/i18n/i18n/pt-BR.json +7 -0
- package/modules/@apostrophecms/i18n/i18n/sk.json +7 -0
- package/modules/@apostrophecms/image/index.js +182 -1
- package/modules/@apostrophecms/image/ui/apos/apps/AposImageRelationshipQueryFilter.js +13 -0
- package/modules/@apostrophecms/image/ui/apos/components/AposImageCropper.vue +460 -0
- package/modules/@apostrophecms/image/ui/apos/components/AposImageRelationshipEditor.vue +510 -0
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +5 -1
- package/modules/@apostrophecms/image/ui/apos/lib/aspectRatios.js +26 -0
- package/modules/@apostrophecms/image-widget/views/widget.html +5 -2
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +45 -1
- package/modules/@apostrophecms/module/index.js +98 -17
- package/modules/@apostrophecms/module/lib/events.js +46 -11
- package/modules/@apostrophecms/page/index.js +55 -22
- package/modules/@apostrophecms/piece-page-type/index.js +1 -0
- package/modules/@apostrophecms/piece-type/index.js +13 -4
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposRelationshipEditor.vue +2 -2
- package/modules/@apostrophecms/rich-text-widget/index.js +1 -3
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +4 -0
- package/modules/@apostrophecms/schema/index.js +79 -73
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +10 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +22 -3
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +72 -36
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +7 -26
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +8 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +45 -15
- package/modules/@apostrophecms/task/index.js +106 -52
- package/modules/@apostrophecms/template/index.js +111 -76
- package/modules/@apostrophecms/template/lib/custom-tags/component.js +42 -22
- package/modules/@apostrophecms/ui/ui/apos/components/AposSelect.vue +61 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +46 -11
- package/modules/@apostrophecms/ui/ui/apos/components/AposSlatList.vue +10 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposTreeHeader.vue +2 -22
- package/modules/@apostrophecms/ui/ui/apos/utils/index.js +9 -0
- package/modules/@apostrophecms/widget-type/index.js +2 -23
- package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidget.vue +1 -1
- package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +20 -1
- package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +0 -9
- package/package.json +16 -12
- package/scripts/lint-i18n.js +2 -2
- package/test/assets.js +2 -1
- package/test/attachments.js +119 -26
- package/test/bundle.js +1 -1
- package/test/content-i18n.js +6 -6
- package/test/docs.js +244 -4
- package/test/draft-published.js +41 -41
- package/test/express.js +1 -1
- package/test/http.js +2 -2
- package/test/images.js +94 -4
- package/test/job.js +1 -1
- package/test/locks.js +1 -1
- package/test/middleware-and-route-order.js +3 -3
- package/test/pages-public-api.js +48 -4
- package/test/pages-rest.js +20 -20
- package/test/pages.js +377 -11
- package/test/parked-pages.js +1 -1
- package/test/permissions.js +10 -10
- package/test/pieces-public-api.js +130 -6
- package/test/pieces.js +247 -60
- package/test/recursionGuard.js +6 -6
- package/test/restApiRoutes.js +6 -6
- package/test/schemaBuilders.js +7 -7
- package/test/schemas.js +59 -59
- package/test/search.js +3 -3
- package/test/soft-redirects.js +13 -13
- package/test/static-i18n.js +1 -1
- package/test/templates.js +10 -10
- package/test/urls.js +2 -2
- package/test/users.js +21 -21
- package/test/utils.js +13 -13
- package/test/widgets.js +2 -2
- package/test-lib/util.js +2 -5
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidget.vue +0 -26
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
// available in the browser for use in the Vue-based admin UI when a user is
|
|
26
26
|
// logged in.
|
|
27
27
|
|
|
28
|
+
const { SemanticAttributes } = require('@opentelemetry/semantic-conventions');
|
|
28
29
|
const _ = require('lodash');
|
|
29
30
|
|
|
30
31
|
module.exports = {
|
|
@@ -444,10 +445,42 @@ module.exports = {
|
|
|
444
445
|
// that point.
|
|
445
446
|
|
|
446
447
|
async sendPage(req, template, data) {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
448
|
+
const telemetry = self.apos.telemetry;
|
|
449
|
+
const spanName = `${self.__meta.name}:sendPage`;
|
|
450
|
+
await telemetry.startActiveSpan(spanName, async (span) => {
|
|
451
|
+
span.setAttribute(SemanticAttributes.CODE_FUNCTION, 'sendPage');
|
|
452
|
+
span.setAttribute(SemanticAttributes.CODE_NAMESPACE, self.__meta.name);
|
|
453
|
+
span.setAttribute(telemetry.Attributes.TEMPLATE, template);
|
|
454
|
+
|
|
455
|
+
try {
|
|
456
|
+
await self.apos.page.emit('beforeSend', req);
|
|
457
|
+
await self.apos.area.loadDeferredWidgets(req);
|
|
458
|
+
req.res.send(
|
|
459
|
+
await self.apos.template.renderPageForModule(req, template, data, self)
|
|
460
|
+
);
|
|
461
|
+
span.setStatus({ code: telemetry.api.SpanStatusCode.OK });
|
|
462
|
+
} catch (err) {
|
|
463
|
+
telemetry.handleError(span, err);
|
|
464
|
+
throw err;
|
|
465
|
+
} finally {
|
|
466
|
+
span.end();
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
},
|
|
470
|
+
|
|
471
|
+
// A cookie in session doesn't mean we can't cache, nor an empty flash or passport object.
|
|
472
|
+
// Other session properties must be assumed to be specific to the user, with a possible
|
|
473
|
+
// impact on the response, and thus mean this request must not be cached.
|
|
474
|
+
// Same rule as in [express-cache-on-demand](https://github.com/apostrophecms/express-cache-on-demand/blob/master/index.js#L102)
|
|
475
|
+
isSafeToCache(req) {
|
|
476
|
+
if (req.user) {
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return Object.entries(req.session).every(([ key, val ]) =>
|
|
481
|
+
key === 'cookie' || (
|
|
482
|
+
(key === 'flash' || key === 'passport') && _.isEmpty(val)
|
|
483
|
+
)
|
|
451
484
|
);
|
|
452
485
|
},
|
|
453
486
|
|
|
@@ -457,21 +490,50 @@ module.exports = {
|
|
|
457
490
|
return;
|
|
458
491
|
}
|
|
459
492
|
|
|
460
|
-
|
|
461
|
-
// Other session properties must be assumed to be specific to the user, with a possible
|
|
462
|
-
// impact on the response, and thus mean this request must not be cached.
|
|
463
|
-
// Same rule as in [express-cache-on-demand](https://github.com/apostrophecms/express-cache-on-demand/blob/master/index.js#L102)
|
|
464
|
-
const isSessionClearForCaching = Object.entries(req.session).every(([ key, val ]) =>
|
|
465
|
-
key === 'cookie' || (
|
|
466
|
-
(key === 'flash' || key === 'passport') && _.isEmpty(val)
|
|
467
|
-
)
|
|
468
|
-
);
|
|
469
|
-
const isSafeToCache = !req.user && isSessionClearForCaching;
|
|
470
|
-
const cacheControlValue = isSafeToCache ? `max-age=${maxAge}` : 'no-store';
|
|
493
|
+
const cacheControlValue = self.isSafeToCache(req) ? `max-age=${maxAge}` : 'no-store';
|
|
471
494
|
|
|
472
495
|
req.res.header('Cache-Control', cacheControlValue);
|
|
473
496
|
},
|
|
474
497
|
|
|
498
|
+
generateETagParts(req, doc) {
|
|
499
|
+
const context = doc || req.data.piece || req.data.page;
|
|
500
|
+
|
|
501
|
+
if (!context || !context.cacheInvalidatedAt) {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const releaseId = self.apos.asset.getReleaseId();
|
|
506
|
+
const cacheInvalidatedAtTimestamp = (new Date(context.cacheInvalidatedAt)).getTime().toString();
|
|
507
|
+
|
|
508
|
+
return [ releaseId, cacheInvalidatedAtTimestamp ];
|
|
509
|
+
},
|
|
510
|
+
|
|
511
|
+
setETag(req, eTagParts) {
|
|
512
|
+
req.res.header('ETag', eTagParts.join(':'));
|
|
513
|
+
},
|
|
514
|
+
|
|
515
|
+
checkETag(req, doc, maxAge) {
|
|
516
|
+
const eTagParts = self.generateETagParts(req, doc);
|
|
517
|
+
|
|
518
|
+
if (!eTagParts || !self.isSafeToCache(req)) {
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const clientETagParts = req.headers['if-none-match'] ? req.headers['if-none-match'].split(':') : [];
|
|
523
|
+
const doesETagMatch = clientETagParts[0] === eTagParts[0] && clientETagParts[1] === eTagParts[1];
|
|
524
|
+
|
|
525
|
+
const now = Date.now();
|
|
526
|
+
const clientETagAge = (now - clientETagParts[2]) / 1000;
|
|
527
|
+
|
|
528
|
+
if (!doesETagMatch || clientETagAge > maxAge) {
|
|
529
|
+
self.setETag(req, [ ...eTagParts, now ]);
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
self.setETag(req, clientETagParts);
|
|
534
|
+
return true;
|
|
535
|
+
},
|
|
536
|
+
|
|
475
537
|
// Call from init once if this module implements the `getBrowserData` method.
|
|
476
538
|
// The data returned by `getBrowserData(req)` will then be available on
|
|
477
539
|
// `apos.modules['your-module-name']` in the browser.
|
|
@@ -649,10 +711,29 @@ module.exports = {
|
|
|
649
711
|
// @apostrophecms/db:reset which
|
|
650
712
|
// must run before most modules are awake
|
|
651
713
|
if (self.apos.argv._[0] === `${self.__meta.name}:${name}`) {
|
|
652
|
-
|
|
714
|
+
const telemetry = self.apos.telemetry;
|
|
715
|
+
const spanName = `task:${self.__meta.name}:${name}`;
|
|
716
|
+
// only this span can be sent to the backend, attach to the ROOT
|
|
717
|
+
await telemetry.startActiveSpan(
|
|
718
|
+
spanName,
|
|
719
|
+
async (span) => {
|
|
720
|
+
span.setAttribute(SemanticAttributes.CODE_FUNCTION, 'executeAfterModuleInitTask');
|
|
721
|
+
span.setAttribute(SemanticAttributes.CODE_NAMESPACE, '@apostrophecms/module');
|
|
722
|
+
span.setAttribute(telemetry.Attributes.TARGET_NAMESPACE, self.__meta.name);
|
|
723
|
+
span.setAttribute(telemetry.Attributes.TARGET_FUNCTION, name);
|
|
724
|
+
try {
|
|
725
|
+
await info.task(self.apos.argv);
|
|
726
|
+
span.setStatus({ code: telemetry.api.SpanStatusCode.OK });
|
|
727
|
+
} catch (err) {
|
|
728
|
+
telemetry.handleError(span, err);
|
|
729
|
+
throw err;
|
|
730
|
+
} finally {
|
|
731
|
+
span.end();
|
|
732
|
+
}
|
|
733
|
+
});
|
|
653
734
|
// In most cases we exit after running a task
|
|
654
735
|
if (info.exitAfter !== false) {
|
|
655
|
-
|
|
736
|
+
await self.apos._exit();
|
|
656
737
|
} else {
|
|
657
738
|
// Provision for @apostrophecms/db:reset which should be
|
|
658
739
|
// followed by normal initialization so all the collections
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
|
+
const { SemanticAttributes } = require('@opentelemetry/semantic-conventions');
|
|
2
3
|
|
|
3
4
|
module.exports = function(self) {
|
|
4
5
|
|
|
@@ -38,18 +39,52 @@ module.exports = function(self) {
|
|
|
38
39
|
}
|
|
39
40
|
];
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
const telemetry = self.apos.telemetry;
|
|
43
|
+
|
|
44
|
+
// Create the "outer" span
|
|
45
|
+
const moduleName = (self.__meta && self.__meta.name) || 'apostrophe';
|
|
46
|
+
const spanEmitName = `event:${moduleName}:${name}`;
|
|
47
|
+
await telemetry.startActiveSpan(spanEmitName, async (spanEmit) => {
|
|
48
|
+
spanEmit.setAttribute(SemanticAttributes.CODE_FUNCTION, 'emit');
|
|
49
|
+
spanEmit.setAttribute(SemanticAttributes.CODE_NAMESPACE, moduleName);
|
|
50
|
+
spanEmit.setAttribute(telemetry.Attributes.EVENT_MODULE, moduleName);
|
|
51
|
+
spanEmit.setAttribute(telemetry.Attributes.EVENT_NAME, name);
|
|
52
|
+
|
|
53
|
+
for (const entry of chain) {
|
|
54
|
+
const handlers = self.apos.eventHandlers[entry.name] && self.apos.eventHandlers[entry.name][name];
|
|
55
|
+
if (handlers) {
|
|
56
|
+
for (const handler of handlers) {
|
|
57
|
+
|
|
58
|
+
// Create an active "inner" span for each handler using the parent as a context
|
|
59
|
+
const spanHandlerName = spanEmitName + `:handler:${handler.moduleName}:${handler.handlerName}`;
|
|
60
|
+
await telemetry.startActiveSpan(spanHandlerName, async (spanHandler) => {
|
|
61
|
+
spanHandler.setAttribute(SemanticAttributes.CODE_FUNCTION, handler.handlerName);
|
|
62
|
+
spanHandler.setAttribute(SemanticAttributes.CODE_NAMESPACE, handler.moduleName);
|
|
63
|
+
spanHandler.setAttribute(telemetry.Attributes.EVENT_MODULE, moduleName);
|
|
64
|
+
spanHandler.setAttribute(telemetry.Attributes.EVENT_NAME, name);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const module = self.apos.modules[handler.moduleName];
|
|
68
|
+
const fn = module.compiledHandlers[entry.name][name][handler.handlerName];
|
|
69
|
+
// Although we have `self` it can't hurt to
|
|
70
|
+
// supply the correct `this`
|
|
71
|
+
await fn.apply(module, args);
|
|
72
|
+
spanHandler.setStatus({ code: telemetry.api.SpanStatusCode.OK });
|
|
73
|
+
spanHandler.end();
|
|
74
|
+
} catch (err) {
|
|
75
|
+
telemetry.handleError(spanHandler, err);
|
|
76
|
+
// Be sure to close the parent span as well
|
|
77
|
+
spanHandler.end();
|
|
78
|
+
spanEmit.end();
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}, spanEmit);
|
|
82
|
+
}
|
|
50
83
|
}
|
|
51
84
|
}
|
|
52
|
-
|
|
85
|
+
|
|
86
|
+
spanEmit.end();
|
|
87
|
+
});
|
|
53
88
|
},
|
|
54
89
|
|
|
55
90
|
// You don't need to call this. It is called for you
|
|
@@ -95,7 +130,7 @@ module.exports = function(self) {
|
|
|
95
130
|
self.apos.eventHandlers[moduleName] = self.apos.eventHandlers[moduleName] || {};
|
|
96
131
|
const eh = self.apos.eventHandlers[moduleName];
|
|
97
132
|
eh[eventName] = eh[eventName] || [];
|
|
98
|
-
if (_.find(eh[eventName], function(item) {
|
|
133
|
+
if (_.find(eh[eventName], function (item) {
|
|
99
134
|
return (item.moduleName === self.__meta.name) && (item.handlerName === handlerName);
|
|
100
135
|
})) {
|
|
101
136
|
// The "event name and method name must differ" rule helps
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { klona } = require('klona');
|
|
4
|
+
const { SemanticAttributes } = require('@opentelemetry/semantic-conventions');
|
|
4
5
|
const expressCacheOnDemand = require('express-cache-on-demand')();
|
|
5
6
|
|
|
6
7
|
module.exports = {
|
|
@@ -119,7 +120,7 @@ module.exports = {
|
|
|
119
120
|
project: self.getAllProjection()
|
|
120
121
|
}).toObject();
|
|
121
122
|
|
|
122
|
-
if (self.options.cache && self.options.cache.api) {
|
|
123
|
+
if (self.options.cache && self.options.cache.api && self.options.cache.api.maxAge) {
|
|
123
124
|
self.setMaxAge(req, self.options.cache.api.maxAge);
|
|
124
125
|
}
|
|
125
126
|
|
|
@@ -142,7 +143,7 @@ module.exports = {
|
|
|
142
143
|
} else {
|
|
143
144
|
const result = await self.getRestQuery(req).and({ level: 0 }).toObject();
|
|
144
145
|
|
|
145
|
-
if (self.options.cache && self.options.cache.api) {
|
|
146
|
+
if (self.options.cache && self.options.cache.api && self.options.cache.api.maxAge) {
|
|
146
147
|
self.setMaxAge(req, self.options.cache.api.maxAge);
|
|
147
148
|
}
|
|
148
149
|
|
|
@@ -178,8 +179,14 @@ module.exports = {
|
|
|
178
179
|
const criteria = self.getIdCriteria(_id);
|
|
179
180
|
const result = await self.getRestQuery(req).and(criteria).toObject();
|
|
180
181
|
|
|
181
|
-
if (self.options.cache && self.options.cache.api) {
|
|
182
|
-
|
|
182
|
+
if (self.options.cache && self.options.cache.api && self.options.cache.api.maxAge) {
|
|
183
|
+
const { maxAge } = self.options.cache.api;
|
|
184
|
+
|
|
185
|
+
if (!self.options.cache.api.etags) {
|
|
186
|
+
self.setMaxAge(req, maxAge);
|
|
187
|
+
} else if (self.checkETag(req, result, maxAge)) {
|
|
188
|
+
return {};
|
|
189
|
+
}
|
|
183
190
|
}
|
|
184
191
|
|
|
185
192
|
if (!result) {
|
|
@@ -1404,6 +1411,18 @@ database.`);
|
|
|
1404
1411
|
} catch (err) {
|
|
1405
1412
|
return await self.serve500Error(req, err);
|
|
1406
1413
|
}
|
|
1414
|
+
|
|
1415
|
+
if (self.options.cache && self.options.cache.page && self.options.cache.page.maxAge) {
|
|
1416
|
+
const { maxAge } = self.options.cache.page;
|
|
1417
|
+
|
|
1418
|
+
if (!self.options.cache.page.etags) {
|
|
1419
|
+
self.setMaxAge(req, maxAge);
|
|
1420
|
+
} else if (self.checkETag(req, undefined, maxAge)) {
|
|
1421
|
+
// Stop there and send a 304 status code; the cached response will be used
|
|
1422
|
+
return res.sendStatus(304);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1407
1426
|
try {
|
|
1408
1427
|
await self.serveDeliver(req, null);
|
|
1409
1428
|
} catch (err) {
|
|
@@ -1415,24 +1434,35 @@ database.`);
|
|
|
1415
1434
|
// of course wins, followed by the parent "folder," and so on up to the
|
|
1416
1435
|
// home page.
|
|
1417
1436
|
async serveGetPage(req) {
|
|
1418
|
-
|
|
1419
|
-
self.
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
if (req.slug !== req.params[0]) {
|
|
1423
|
-
req.redirect = req.slug;
|
|
1424
|
-
}
|
|
1425
|
-
const builders = self.getServePageBuilders();
|
|
1426
|
-
const query = self.find(req);
|
|
1427
|
-
query.applyBuilders(builders);
|
|
1428
|
-
self.matchPageAndPrefixes(query, req.slug);
|
|
1429
|
-
await self.emit('serveQuery', query);
|
|
1430
|
-
req.data.bestPage = await query.toObject();
|
|
1431
|
-
self.evaluatePageMatch(req);
|
|
1437
|
+
const spanName = `${self.__meta.name}:serveGetPage`;
|
|
1438
|
+
await self.apos.telemetry.startActiveSpan(spanName, async (span) => {
|
|
1439
|
+
span.setAttribute(SemanticAttributes.CODE_FUNCTION, 'serveGetPage');
|
|
1440
|
+
span.setAttribute(SemanticAttributes.CODE_NAMESPACE, self.__meta.name);
|
|
1432
1441
|
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1442
|
+
try {
|
|
1443
|
+
req.slug = req.params[0];
|
|
1444
|
+
self.normalizeSlug(req);
|
|
1445
|
+
// Had to change the URL, so redirect to it. TODO: this
|
|
1446
|
+
// contains an assumption that we are mounted at /
|
|
1447
|
+
if (req.slug !== req.params[0]) {
|
|
1448
|
+
req.redirect = req.slug;
|
|
1449
|
+
}
|
|
1450
|
+
const builders = self.getServePageBuilders();
|
|
1451
|
+
const query = self.find(req);
|
|
1452
|
+
query.applyBuilders(builders);
|
|
1453
|
+
self.matchPageAndPrefixes(query, req.slug);
|
|
1454
|
+
await self.emit('serveQuery', query);
|
|
1455
|
+
req.data.bestPage = await query.toObject();
|
|
1456
|
+
self.evaluatePageMatch(req);
|
|
1457
|
+
|
|
1458
|
+
span.setStatus({ code: self.apos.telemetry.api.SpanStatusCode.OK });
|
|
1459
|
+
} catch (err) {
|
|
1460
|
+
self.apos.telemetry.handleError(span, err);
|
|
1461
|
+
throw err;
|
|
1462
|
+
} finally {
|
|
1463
|
+
span.end();
|
|
1464
|
+
}
|
|
1465
|
+
});
|
|
1436
1466
|
},
|
|
1437
1467
|
// Normalize req.slug to account for unneeded trailing whitespace,
|
|
1438
1468
|
// trailing slashes other than the root, and double slash based open
|
|
@@ -2108,7 +2138,10 @@ database.`);
|
|
|
2108
2138
|
_id: null
|
|
2109
2139
|
});
|
|
2110
2140
|
} else {
|
|
2111
|
-
query.project(
|
|
2141
|
+
query.project({
|
|
2142
|
+
...self.options.publicApiProjection,
|
|
2143
|
+
cacheInvalidatedAt: 1
|
|
2144
|
+
});
|
|
2112
2145
|
}
|
|
2113
2146
|
}
|
|
2114
2147
|
return query;
|
|
@@ -201,7 +201,7 @@ module.exports = {
|
|
|
201
201
|
result.counts = query.get('countsResults');
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
if (self.options.cache && self.options.cache.api) {
|
|
204
|
+
if (self.options.cache && self.options.cache.api && self.options.cache.api.maxAge) {
|
|
205
205
|
self.setMaxAge(req, self.options.cache.api.maxAge);
|
|
206
206
|
}
|
|
207
207
|
|
|
@@ -215,8 +215,14 @@ module.exports = {
|
|
|
215
215
|
self.publicApiCheck(req);
|
|
216
216
|
const doc = await self.getRestQuery(req).and({ _id }).toObject();
|
|
217
217
|
|
|
218
|
-
if (self.options.cache && self.options.cache.api) {
|
|
219
|
-
|
|
218
|
+
if (self.options.cache && self.options.cache.api && self.options.cache.api.maxAge) {
|
|
219
|
+
const { maxAge } = self.options.cache.api;
|
|
220
|
+
|
|
221
|
+
if (!self.options.cache.api.etags) {
|
|
222
|
+
self.setMaxAge(req, maxAge);
|
|
223
|
+
} else if (self.checkETag(req, doc, maxAge)) {
|
|
224
|
+
return {};
|
|
225
|
+
}
|
|
220
226
|
}
|
|
221
227
|
|
|
222
228
|
if (!doc) {
|
|
@@ -917,7 +923,10 @@ module.exports = {
|
|
|
917
923
|
_id: null
|
|
918
924
|
});
|
|
919
925
|
} else if (!query.state.project) {
|
|
920
|
-
query.project(
|
|
926
|
+
query.project({
|
|
927
|
+
...self.options.publicApiProjection,
|
|
928
|
+
cacheInvalidatedAt: 1
|
|
929
|
+
});
|
|
921
930
|
}
|
|
922
931
|
}
|
|
923
932
|
return query;
|
|
@@ -72,8 +72,8 @@ export default {
|
|
|
72
72
|
original: this.value,
|
|
73
73
|
docFields: {
|
|
74
74
|
data: {
|
|
75
|
-
...((this.value != null) ? this.value
|
|
76
|
-
Object.fromEntries(
|
|
75
|
+
...((this.value != null) ? this.value
|
|
76
|
+
: Object.fromEntries(
|
|
77
77
|
this.schema.map(field =>
|
|
78
78
|
[ field.name, (field.def !== undefined) ? klona(field.def) : null ]
|
|
79
79
|
)
|
|
@@ -44,8 +44,7 @@ module.exports = {
|
|
|
44
44
|
},
|
|
45
45
|
defaultOptions: {},
|
|
46
46
|
components: {
|
|
47
|
-
widgetEditor: 'AposRichTextWidgetEditor'
|
|
48
|
-
widget: 'AposRichTextWidget'
|
|
47
|
+
widgetEditor: 'AposRichTextWidgetEditor'
|
|
49
48
|
},
|
|
50
49
|
editorTools: {
|
|
51
50
|
styles: {
|
|
@@ -415,7 +414,6 @@ module.exports = {
|
|
|
415
414
|
|
|
416
415
|
const finalData = {
|
|
417
416
|
...initialData,
|
|
418
|
-
components: self.options.components,
|
|
419
417
|
tools: self.options.editorTools,
|
|
420
418
|
defaultOptions: self.options.defaultOptions,
|
|
421
419
|
tiptapTextCommands: self.options.tiptapTextCommands,
|