@endora-commerce/mod-settings 0.100.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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -0
  3. package/dist/admin/api/settings-client.d.ts +47 -0
  4. package/dist/admin/api/settings-client.d.ts.map +1 -0
  5. package/dist/admin/api/settings-client.js +41 -0
  6. package/dist/admin/api/settings-client.js.map +1 -0
  7. package/dist/admin/components/ActivationPointerRow.d.ts +21 -0
  8. package/dist/admin/components/ActivationPointerRow.d.ts.map +1 -0
  9. package/dist/admin/components/ActivationPointerRow.js +28 -0
  10. package/dist/admin/components/ActivationPointerRow.js.map +1 -0
  11. package/dist/admin/components/AssetIdSettingInput.d.ts +12 -0
  12. package/dist/admin/components/AssetIdSettingInput.d.ts.map +1 -0
  13. package/dist/admin/components/AssetIdSettingInput.js +10 -0
  14. package/dist/admin/components/AssetIdSettingInput.js.map +1 -0
  15. package/dist/admin/components/ConfigurationReferenceInput.d.ts +39 -0
  16. package/dist/admin/components/ConfigurationReferenceInput.d.ts.map +1 -0
  17. package/dist/admin/components/ConfigurationReferenceInput.js +74 -0
  18. package/dist/admin/components/ConfigurationReferenceInput.js.map +1 -0
  19. package/dist/admin/components/ConflictBanner.d.ts +13 -0
  20. package/dist/admin/components/ConflictBanner.d.ts.map +1 -0
  21. package/dist/admin/components/ConflictBanner.js +12 -0
  22. package/dist/admin/components/ConflictBanner.js.map +1 -0
  23. package/dist/admin/components/ImageSettingInput.d.ts +34 -0
  24. package/dist/admin/components/ImageSettingInput.d.ts.map +1 -0
  25. package/dist/admin/components/ImageSettingInput.js +36 -0
  26. package/dist/admin/components/ImageSettingInput.js.map +1 -0
  27. package/dist/admin/components/QuoteRequestsSettingsTab.d.ts +3 -0
  28. package/dist/admin/components/QuoteRequestsSettingsTab.d.ts.map +1 -0
  29. package/dist/admin/components/QuoteRequestsSettingsTab.js +69 -0
  30. package/dist/admin/components/QuoteRequestsSettingsTab.js.map +1 -0
  31. package/dist/admin/components/SellerCompanyDataInput.d.ts +22 -0
  32. package/dist/admin/components/SellerCompanyDataInput.d.ts.map +1 -0
  33. package/dist/admin/components/SellerCompanyDataInput.js +53 -0
  34. package/dist/admin/components/SellerCompanyDataInput.js.map +1 -0
  35. package/dist/admin/components/SettingRowEditor.d.ts +55 -0
  36. package/dist/admin/components/SettingRowEditor.d.ts.map +1 -0
  37. package/dist/admin/components/SettingRowEditor.js +160 -0
  38. package/dist/admin/components/SettingRowEditor.js.map +1 -0
  39. package/dist/admin/index.d.ts +39 -0
  40. package/dist/admin/index.d.ts.map +1 -0
  41. package/dist/admin/index.js +87 -0
  42. package/dist/admin/index.js.map +1 -0
  43. package/dist/admin/pages/CachePage.d.ts +14 -0
  44. package/dist/admin/pages/CachePage.d.ts.map +1 -0
  45. package/dist/admin/pages/CachePage.js +124 -0
  46. package/dist/admin/pages/CachePage.js.map +1 -0
  47. package/dist/admin/pages/GroupsPage.d.ts +17 -0
  48. package/dist/admin/pages/GroupsPage.d.ts.map +1 -0
  49. package/dist/admin/pages/GroupsPage.js +84 -0
  50. package/dist/admin/pages/GroupsPage.js.map +1 -0
  51. package/dist/admin/pages/SettingsPage.d.ts +20 -0
  52. package/dist/admin/pages/SettingsPage.d.ts.map +1 -0
  53. package/dist/admin/pages/SettingsPage.js +453 -0
  54. package/dist/admin/pages/SettingsPage.js.map +1 -0
  55. package/dist/backend/cli/cache-clear.d.ts +23 -0
  56. package/dist/backend/cli/cache-clear.d.ts.map +1 -0
  57. package/dist/backend/cli/cache-clear.js +37 -0
  58. package/dist/backend/cli/cache-clear.js.map +1 -0
  59. package/dist/backend/index.d.ts +100 -0
  60. package/dist/backend/index.d.ts.map +1 -0
  61. package/dist/backend/index.js +125 -0
  62. package/dist/backend/index.js.map +1 -0
  63. package/dist/backend/routes.admin.d.ts +308 -0
  64. package/dist/backend/routes.admin.d.ts.map +1 -0
  65. package/dist/backend/routes.admin.js +188 -0
  66. package/dist/backend/routes.admin.js.map +1 -0
  67. package/dist/backend/routes.cache.d.ts +20 -0
  68. package/dist/backend/routes.cache.d.ts.map +1 -0
  69. package/dist/backend/routes.cache.js +22 -0
  70. package/dist/backend/routes.cache.js.map +1 -0
  71. package/dist/backend/routes.homepage.d.ts +15 -0
  72. package/dist/backend/routes.homepage.d.ts.map +1 -0
  73. package/dist/backend/routes.homepage.js +20 -0
  74. package/dist/backend/routes.homepage.js.map +1 -0
  75. package/dist/backend/routes.product-card-buttons.d.ts +15 -0
  76. package/dist/backend/routes.product-card-buttons.d.ts.map +1 -0
  77. package/dist/backend/routes.product-card-buttons.js +21 -0
  78. package/dist/backend/routes.product-card-buttons.js.map +1 -0
  79. package/dist/backend/routes.speculation-rules.d.ts +16 -0
  80. package/dist/backend/routes.speculation-rules.d.ts.map +1 -0
  81. package/dist/backend/routes.speculation-rules.js +21 -0
  82. package/dist/backend/routes.speculation-rules.js.map +1 -0
  83. package/dist/backend/routes.storefront.d.ts +15 -0
  84. package/dist/backend/routes.storefront.d.ts.map +1 -0
  85. package/dist/backend/routes.storefront.js +20 -0
  86. package/dist/backend/routes.storefront.js.map +1 -0
  87. package/dist/backend/services/cache-admin.service.d.ts +83 -0
  88. package/dist/backend/services/cache-admin.service.d.ts.map +1 -0
  89. package/dist/backend/services/cache-admin.service.js +148 -0
  90. package/dist/backend/services/cache-admin.service.js.map +1 -0
  91. package/dist/backend/services/homepage-resolver.d.ts +25 -0
  92. package/dist/backend/services/homepage-resolver.d.ts.map +1 -0
  93. package/dist/backend/services/homepage-resolver.js +31 -0
  94. package/dist/backend/services/homepage-resolver.js.map +1 -0
  95. package/dist/backend/services/product-card-buttons-resolver.d.ts +25 -0
  96. package/dist/backend/services/product-card-buttons-resolver.d.ts.map +1 -0
  97. package/dist/backend/services/product-card-buttons-resolver.js +37 -0
  98. package/dist/backend/services/product-card-buttons-resolver.js.map +1 -0
  99. package/dist/backend/services/registered-settings-manifests.d.ts +45 -0
  100. package/dist/backend/services/registered-settings-manifests.d.ts.map +1 -0
  101. package/dist/backend/services/registered-settings-manifests.js +55 -0
  102. package/dist/backend/services/registered-settings-manifests.js.map +1 -0
  103. package/dist/backend/services/setting-write-validators.d.ts +43 -0
  104. package/dist/backend/services/setting-write-validators.d.ts.map +1 -0
  105. package/dist/backend/services/setting-write-validators.js +43 -0
  106. package/dist/backend/services/setting-write-validators.js.map +1 -0
  107. package/dist/backend/services/settings-admin.service.d.ts +206 -0
  108. package/dist/backend/services/settings-admin.service.d.ts.map +1 -0
  109. package/dist/backend/services/settings-admin.service.js +642 -0
  110. package/dist/backend/services/settings-admin.service.js.map +1 -0
  111. package/dist/backend/services/shop-info-resolver.d.ts +15 -0
  112. package/dist/backend/services/shop-info-resolver.d.ts.map +1 -0
  113. package/dist/backend/services/shop-info-resolver.js +44 -0
  114. package/dist/backend/services/shop-info-resolver.js.map +1 -0
  115. package/dist/backend/services/speculation-rules-resolver.d.ts +28 -0
  116. package/dist/backend/services/speculation-rules-resolver.d.ts.map +1 -0
  117. package/dist/backend/services/speculation-rules-resolver.js +39 -0
  118. package/dist/backend/services/speculation-rules-resolver.js.map +1 -0
  119. package/dist/manifest.d.ts +194 -0
  120. package/dist/manifest.d.ts.map +1 -0
  121. package/dist/manifest.js +379 -0
  122. package/dist/manifest.js.map +1 -0
  123. package/docs/settings/index.md +171 -0
  124. package/i18n/en.json +167 -0
  125. package/i18n/pl.json +167 -0
  126. package/package.json +96 -0
  127. package/tailwind.css +14 -0
@@ -0,0 +1,188 @@
1
+ import { ERROR_CODES, GroupCreateRequestSchema, GroupUpdateRequestSchema, ResetValueQuerySchema, SetValueRequestSchema, SettingsListQuerySchema, } from '@endora-commerce/contracts';
2
+ import { secretValueIsSet } from '@endora-commerce/platform/kernel';
3
+ export async function registerSettingsAdminRoutes(app, deps) {
4
+ const { adminService, requireAdmin, resolveAdminAuditContext } = deps;
5
+ const auditContext = (request) => {
6
+ const ctx = resolveAdminAuditContext?.(request) ?? {
7
+ actorAdminUserId: null,
8
+ };
9
+ const rid = request.headers['x-request-id'];
10
+ return {
11
+ ...ctx,
12
+ requestId: typeof rid === 'string' ? rid : null,
13
+ };
14
+ };
15
+ function setEtag(reply, version) {
16
+ reply.header('etag', `"${version}"`);
17
+ }
18
+ function serializeSetting(setting, values, classification = { editable: true, activationControl: false }) {
19
+ // Secret settings are write-only (feature 043, FR-021): every read
20
+ // replaces stored values with null + isSet indicators. The plaintext (or
21
+ // its ciphertext envelope) never leaves the backend through this API.
22
+ const isSecret = setting.valueType === 'secret';
23
+ return {
24
+ id: setting.id,
25
+ code: setting.code,
26
+ name: setting.name,
27
+ description: setting.description ?? null,
28
+ valueType: setting.valueType,
29
+ ownerModule: setting.ownerModule,
30
+ salesChannelCodes: setting.salesChannels.getItems().map((c) => c.code),
31
+ enumOptions: setting.enumOptions ?? null,
32
+ configurationType: setting.configurationType ?? null,
33
+ defaultValue: isSecret ? null : setting.defaultValue,
34
+ globalValue: isSecret ? null : setting.globalValue ?? null,
35
+ ...(isSecret ? { globalValueIsSet: secretValueIsSet(setting.globalValue) } : {}),
36
+ valuesByChannel: values.map((v) => ({
37
+ salesChannelId: v.salesChannel.id,
38
+ salesChannelCode: v.salesChannel.code,
39
+ value: isSecret ? null : v.value,
40
+ ...(isSecret ? { isSet: secretValueIsSet(v.value) } : {}),
41
+ updatedAt: v.updatedAt.toISOString(),
42
+ })),
43
+ version: adminService.computeSettingVersion(setting, values),
44
+ editable: classification.editable,
45
+ activationControl: classification.activationControl,
46
+ };
47
+ }
48
+ function serializeGroup(group, settings) {
49
+ return {
50
+ id: group.id,
51
+ code: group.code,
52
+ name: group.name,
53
+ isSystemProtected: group.isSystemProtected,
54
+ ownerModule: group.ownerModule,
55
+ salesChannelCodes: group.salesChannels.getItems().map((c) => c.code),
56
+ settings: settings.map((s) => serializeSetting(s.setting, s.values, {
57
+ editable: s.editable,
58
+ activationControl: s.activationControl,
59
+ })),
60
+ };
61
+ }
62
+ // --------------------------------------------------------------------
63
+ // List + detail
64
+ // --------------------------------------------------------------------
65
+ app.get('/api/v1/admin/settings', { preHandler: requireAdmin('settings:read') }, async (request) => {
66
+ const q = SettingsListQuerySchema.parse(request.query ?? {});
67
+ const filter = q.groupCode ? { groupCode: q.groupCode } : undefined;
68
+ const result = await adminService.listGroups(filter);
69
+ return { groups: result.groups.map((g) => serializeGroup(g.group, g.settings)) };
70
+ });
71
+ app.get('/api/v1/admin/settings/:code', { preHandler: requireAdmin('settings:read') }, async (request, reply) => {
72
+ const { setting, values, version, editable, activationControl } = await adminService.getSettingByCode(request.params.code);
73
+ setEtag(reply, version);
74
+ return serializeSetting(setting, values, { editable, activationControl });
75
+ });
76
+ // --------------------------------------------------------------------
77
+ // Value writes
78
+ // --------------------------------------------------------------------
79
+ app.put('/api/v1/admin/settings/:code/value', { preHandler: requireAdmin('settings:write') }, async (request, reply) => {
80
+ const body = SetValueRequestSchema.parse(request.body);
81
+ const expectedVersion = (request.body ?? {}).expectedVersion ??
82
+ parseIfMatch(request);
83
+ const ctx = auditContext(request);
84
+ let result;
85
+ if (body.scope === 'all') {
86
+ result = await adminService.setValueForAllChannels(request.params.code, body.value, expectedVersion, ctx);
87
+ }
88
+ else {
89
+ result = await adminService.setValueForSubset(request.params.code, body.salesChannelCodes, body.value, expectedVersion, ctx);
90
+ }
91
+ setEtag(reply, result.newVersion);
92
+ // Refresh full detail for the response.
93
+ const detail = await adminService.getSettingByCode(request.params.code);
94
+ return serializeSetting(detail.setting, detail.values, {
95
+ editable: detail.editable,
96
+ activationControl: detail.activationControl,
97
+ });
98
+ });
99
+ app.delete('/api/v1/admin/settings/:code/values', { preHandler: requireAdmin('settings:write') }, async (request, reply) => {
100
+ const q = ResetValueQuerySchema.parse(request.query ?? {});
101
+ const channelCodes = q.salesChannelCodes
102
+ ? q.salesChannelCodes.split(',').map((c) => c.trim()).filter(Boolean)
103
+ : undefined;
104
+ const ctx = auditContext(request);
105
+ const result = await adminService.resetValues(request.params.code, channelCodes, ctx);
106
+ setEtag(reply, result.newVersion);
107
+ const detail = await adminService.getSettingByCode(request.params.code);
108
+ return serializeSetting(detail.setting, detail.values, {
109
+ editable: detail.editable,
110
+ activationControl: detail.activationControl,
111
+ });
112
+ });
113
+ // --------------------------------------------------------------------
114
+ // Group CRUD
115
+ // --------------------------------------------------------------------
116
+ app.get('/api/v1/admin/settings/groups', { preHandler: requireAdmin('settings:read') }, async () => {
117
+ const result = await adminService.listGroups();
118
+ return {
119
+ groups: result.groups.map((g) => ({
120
+ id: g.group.id,
121
+ code: g.group.code,
122
+ name: g.group.name,
123
+ isSystemProtected: g.group.isSystemProtected,
124
+ ownerModule: g.group.ownerModule,
125
+ salesChannelCodes: g.group.salesChannels.getItems().map((c) => c.code),
126
+ settingCount: g.settings.length,
127
+ updatedAt: g.group.updatedAt.toISOString(),
128
+ })),
129
+ };
130
+ });
131
+ app.post('/api/v1/admin/settings/groups', { preHandler: requireAdmin('settings:write') }, async (request, reply) => {
132
+ const body = GroupCreateRequestSchema.parse(request.body);
133
+ const ctx = auditContext(request);
134
+ const created = await adminService.createGroup({
135
+ code: body.code,
136
+ name: body.name,
137
+ ...(body.salesChannelCodes !== undefined
138
+ ? { salesChannelCodes: body.salesChannelCodes }
139
+ : {}),
140
+ }, ctx);
141
+ reply.code(201);
142
+ setEtag(reply, created.updatedAt.toISOString());
143
+ return {
144
+ id: created.id,
145
+ code: created.code,
146
+ name: created.name,
147
+ isSystemProtected: created.isSystemProtected,
148
+ ownerModule: created.ownerModule,
149
+ salesChannelCodes: created.salesChannels.getItems().map((c) => c.code),
150
+ };
151
+ });
152
+ app.patch('/api/v1/admin/settings/groups/:code', { preHandler: requireAdmin('settings:write') }, async (request, reply) => {
153
+ const body = GroupUpdateRequestSchema.parse(request.body);
154
+ const expectedVersion = (request.body ?? {}).expectedVersion ??
155
+ parseIfMatch(request);
156
+ const ctx = auditContext(request);
157
+ const patch = {};
158
+ if (body.name !== undefined)
159
+ patch.name = body.name;
160
+ if (body.salesChannelCodes !== undefined) {
161
+ patch.salesChannelCodes = body.salesChannelCodes;
162
+ }
163
+ const updated = await adminService.updateGroup(request.params.code, patch, expectedVersion, ctx);
164
+ setEtag(reply, updated.updatedAt.toISOString());
165
+ return {
166
+ id: updated.id,
167
+ code: updated.code,
168
+ name: updated.name,
169
+ isSystemProtected: updated.isSystemProtected,
170
+ ownerModule: updated.ownerModule,
171
+ salesChannelCodes: updated.salesChannels.getItems().map((c) => c.code),
172
+ };
173
+ });
174
+ app.delete('/api/v1/admin/settings/groups/:code', { preHandler: requireAdmin('settings:write') }, async (request, reply) => {
175
+ const ctx = auditContext(request);
176
+ await adminService.deleteGroup(request.params.code, ctx);
177
+ reply.code(204);
178
+ });
179
+ }
180
+ function parseIfMatch(request) {
181
+ const header = request.headers['if-match'];
182
+ if (!header || typeof header !== 'string')
183
+ return null;
184
+ return header.replace(/^"|"$/g, '');
185
+ }
186
+ // Export a typed error helper so other modules can import the shape if needed.
187
+ export const _settingsRoutesErrors = ERROR_CODES;
188
+ //# sourceMappingURL=routes.admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { CacheAdminService } from './services/cache-admin.service.js';
3
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
4
+ /**
5
+ * Admin cache-maintenance routes — lets an operator flush selected Redis cache
6
+ * namespaces from the Admin UI so content / settings changes appear without
7
+ * waiting for TTL expiry.
8
+ *
9
+ * GET /api/v1/admin/cache/namespaces — list clearable namespaces
10
+ * POST /api/v1/admin/cache/clear — clear selected (or all) namespaces
11
+ *
12
+ * Gated by `settings:write` — clearing caches is a privileged maintenance
13
+ * action on par with editing platform settings.
14
+ */
15
+ export interface SettingsCacheDeps {
16
+ cacheAdminService: CacheAdminService;
17
+ requireAdmin: RequireAdminFactory;
18
+ }
19
+ export declare function registerSettingsCacheRoutes(app: FastifyInstance, deps: SettingsCacheDeps): Promise<void>;
20
+ //# sourceMappingURL=routes.cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.cache.d.ts","sourceRoot":"","sources":["../../src/backend/routes.cache.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,YAAY,EAAE,mBAAmB,CAAC;CACnC;AAED,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
@@ -0,0 +1,22 @@
1
+ import { ClearCacheRequestSchema } from '@endora-commerce/contracts';
2
+ export async function registerSettingsCacheRoutes(app, deps) {
3
+ const { cacheAdminService, requireAdmin } = deps;
4
+ app.get('/api/v1/admin/cache/namespaces', { preHandler: requireAdmin('settings:write') }, async () => ({
5
+ data: cacheAdminService.listNamespaces(),
6
+ cacheEnabled: cacheAdminService.enabled,
7
+ }));
8
+ // Per-route ceiling on top of the global one in `http/server.ts`. Clearing
9
+ // leaves both cache layers cold platform-wide, so every subsequent setting
10
+ // read goes to PostgreSQL until they refill — cheap queries, but the
11
+ // connection pool is the scarce resource, and it is what a repeated clear
12
+ // exhausts. A human pressing the button a few times while diagnosing stays
13
+ // well inside this; a script in a loop does not, which is the case worth
14
+ // stopping.
15
+ const rateLimit = { max: 6, timeWindow: '1 minute' };
16
+ app.post('/api/v1/admin/cache/clear', { preHandler: requireAdmin('settings:write'), config: { rateLimit } }, async (request) => {
17
+ const body = ClearCacheRequestSchema.parse(request.body ?? {});
18
+ const result = await cacheAdminService.clear(body.namespaces);
19
+ return { data: result };
20
+ });
21
+ }
22
+ //# sourceMappingURL=routes.cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.cache.js","sourceRoot":"","sources":["../../src/backend/routes.cache.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAoBrE,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,GAAoB,EACpB,IAAuB;IAEvB,MAAM,EAAE,iBAAiB,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAEjD,GAAG,CAAC,GAAG,CACL,gCAAgC,EAChC,EAAE,UAAU,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,EAC9C,KAAK,IAAI,EAAE,CAAC,CAAC;QACX,IAAI,EAAE,iBAAiB,CAAC,cAAc,EAAE;QACxC,YAAY,EAAE,iBAAiB,CAAC,OAAO;KACxC,CAAC,CACH,CAAC;IAEF,2EAA2E;IAC3E,2EAA2E;IAC3E,qEAAqE;IACrE,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,YAAY;IACZ,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;IAErD,GAAG,CAAC,IAAI,CACN,2BAA2B,EAC3B,EAAE,UAAU,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,EAAE,EACrE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,IAAI,GAAG,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { HomepageResolver } from './services/homepage-resolver.js';
3
+ /**
4
+ * Public storefront read for the home-page configuration.
5
+ *
6
+ * GET /api/v1/storefront/homepage
7
+ *
8
+ * Returns the CMS page slug an operator selected as the home page for the
9
+ * request's sales channel (from `x-sales-channel`, falling back to the
10
+ * system-default channel), or null when none is configured.
11
+ */
12
+ export declare function registerSettingsHomepageRoutes(app: FastifyInstance, deps: {
13
+ homepageResolver: HomepageResolver;
14
+ }): Promise<void>;
15
+ //# sourceMappingURL=routes.homepage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.homepage.d.ts","sourceRoot":"","sources":["../../src/backend/routes.homepage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExE;;;;;;;;GAQG;AACH,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE;IAAE,gBAAgB,EAAE,gBAAgB,CAAA;CAAE,GAC3C,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -0,0 +1,20 @@
1
+ import { getResolvedChannel } from '@endora-commerce/platform/kernel';
2
+ /**
3
+ * Public storefront read for the home-page configuration.
4
+ *
5
+ * GET /api/v1/storefront/homepage
6
+ *
7
+ * Returns the CMS page slug an operator selected as the home page for the
8
+ * request's sales channel (from `x-sales-channel`, falling back to the
9
+ * system-default channel), or null when none is configured.
10
+ */
11
+ export async function registerSettingsHomepageRoutes(app, deps) {
12
+ app.get('/api/v1/storefront/homepage', async (request) => {
13
+ // The id, not the code: the channel is already resolved, and handing the
14
+ // resolver a code made it re-resolve one (feature 075 / D-87).
15
+ const salesChannelId = getResolvedChannel(request).id;
16
+ const data = await deps.homepageResolver.resolve(salesChannelId);
17
+ return { data };
18
+ });
19
+ }
20
+ //# sourceMappingURL=routes.homepage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.homepage.js","sourceRoot":"","sources":["../../src/backend/routes.homepage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAGtE;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,GAAoB,EACpB,IAA4C;IAE5C,GAAG,CAAC,GAAG,CAAC,6BAA6B,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACvD,yEAAyE;QACzE,+DAA+D;QAC/D,MAAM,cAAc,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACjE,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { ProductCardButtonsResolver } from './services/product-card-buttons-resolver.js';
3
+ /**
4
+ * Public storefront read for the product-card button visibility toggles.
5
+ *
6
+ * GET /api/v1/storefront/settings/product-card-buttons
7
+ *
8
+ * Returns `{ showAddToCart, showAddToShoppingList, showAddToQuote }` for the
9
+ * request's sales channel (from `x-sales-channel`, falling back to the
10
+ * system-default channel). Each flag defaults to true on any read error.
11
+ */
12
+ export declare function registerSettingsProductCardButtonsRoutes(app: FastifyInstance, deps: {
13
+ productCardButtonsResolver: ProductCardButtonsResolver;
14
+ }): Promise<void>;
15
+ //# sourceMappingURL=routes.product-card-buttons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.product-card-buttons.d.ts","sourceRoot":"","sources":["../../src/backend/routes.product-card-buttons.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,6CAA6C,CAAC;AAE9F;;;;;;;;GAQG;AACH,wBAAsB,wCAAwC,CAC5D,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE;IAAE,0BAA0B,EAAE,0BAA0B,CAAA;CAAE,GAC/D,OAAO,CAAC,IAAI,CAAC,CASf"}
@@ -0,0 +1,21 @@
1
+ import { getResolvedChannel } from '@endora-commerce/platform/kernel';
2
+ /**
3
+ * Public storefront read for the product-card button visibility toggles.
4
+ *
5
+ * GET /api/v1/storefront/settings/product-card-buttons
6
+ *
7
+ * Returns `{ showAddToCart, showAddToShoppingList, showAddToQuote }` for the
8
+ * request's sales channel (from `x-sales-channel`, falling back to the
9
+ * system-default channel). Each flag defaults to true on any read error.
10
+ */
11
+ export async function registerSettingsProductCardButtonsRoutes(app, deps) {
12
+ app.get('/api/v1/storefront/settings/product-card-buttons', async (request, reply) => {
13
+ reply.header('cache-control', 'public, max-age=60');
14
+ // The id, not the code: the channel is already resolved, and handing the
15
+ // resolver a code made it re-resolve one (feature 075 / D-87).
16
+ const salesChannelId = getResolvedChannel(request).id;
17
+ const data = await deps.productCardButtonsResolver.resolve(salesChannelId);
18
+ return { data };
19
+ });
20
+ }
21
+ //# sourceMappingURL=routes.product-card-buttons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.product-card-buttons.js","sourceRoot":"","sources":["../../src/backend/routes.product-card-buttons.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAGtE;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,wCAAwC,CAC5D,GAAoB,EACpB,IAAgE;IAEhE,GAAG,CAAC,GAAG,CAAC,kDAAkD,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACnF,KAAK,CAAC,MAAM,CAAC,eAAe,EAAE,oBAAoB,CAAC,CAAC;QACpD,yEAAyE;QACzE,+DAA+D;QAC/D,MAAM,cAAc,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QAC3E,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { SpeculationRulesResolver } from './services/speculation-rules-resolver.js';
3
+ /**
4
+ * Public storefront read for the Speculation Rules configuration.
5
+ *
6
+ * GET /api/v1/storefront/speculation-rules
7
+ *
8
+ * Resolves the `storefront.speculation_rules.*` settings for the request's
9
+ * sales channel (from `x-sales-channel`, falling back to the system-default
10
+ * channel). Returns `{ enabled, eagerness }`; a settings hiccup falls back to
11
+ * enabled + 'moderate' inside the resolver so a public page never 500s.
12
+ */
13
+ export declare function registerSettingsSpeculationRulesRoutes(app: FastifyInstance, deps: {
14
+ speculationRulesResolver: SpeculationRulesResolver;
15
+ }): Promise<void>;
16
+ //# sourceMappingURL=routes.speculation-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.speculation-rules.d.ts","sourceRoot":"","sources":["../../src/backend/routes.speculation-rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAEzF;;;;;;;;;GASG;AACH,wBAAsB,sCAAsC,CAC1D,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE;IAAE,wBAAwB,EAAE,wBAAwB,CAAA;CAAE,GAC3D,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -0,0 +1,21 @@
1
+ import { getResolvedChannel } from '@endora-commerce/platform/kernel';
2
+ /**
3
+ * Public storefront read for the Speculation Rules configuration.
4
+ *
5
+ * GET /api/v1/storefront/speculation-rules
6
+ *
7
+ * Resolves the `storefront.speculation_rules.*` settings for the request's
8
+ * sales channel (from `x-sales-channel`, falling back to the system-default
9
+ * channel). Returns `{ enabled, eagerness }`; a settings hiccup falls back to
10
+ * enabled + 'moderate' inside the resolver so a public page never 500s.
11
+ */
12
+ export async function registerSettingsSpeculationRulesRoutes(app, deps) {
13
+ app.get('/api/v1/storefront/speculation-rules', async (request) => {
14
+ // The id, not the code: the channel is already resolved, and handing the
15
+ // resolver a code made it re-resolve one (feature 075 / D-87).
16
+ const salesChannelId = getResolvedChannel(request).id;
17
+ const data = await deps.speculationRulesResolver.resolve(salesChannelId);
18
+ return { data };
19
+ });
20
+ }
21
+ //# sourceMappingURL=routes.speculation-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.speculation-rules.js","sourceRoot":"","sources":["../../src/backend/routes.speculation-rules.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAGtE;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,sCAAsC,CAC1D,GAAoB,EACpB,IAA4D;IAE5D,GAAG,CAAC,GAAG,CAAC,sCAAsC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChE,yEAAyE;QACzE,+DAA+D;QAC/D,MAAM,cAAc,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACzE,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { ShopInfoResolver } from './services/shop-info-resolver.js';
3
+ /**
4
+ * Public storefront read for the shop / company contact information.
5
+ *
6
+ * GET /api/v1/storefront/shop-info
7
+ *
8
+ * Resolves the `shop.*` settings for the request's sales channel (from the
9
+ * `x-sales-channel` header, falling back to the system-default channel). Every
10
+ * field is always present as a string; unset values come back empty.
11
+ */
12
+ export declare function registerSettingsStorefrontRoutes(app: FastifyInstance, deps: {
13
+ shopInfoResolver: ShopInfoResolver;
14
+ }): Promise<void>;
15
+ //# sourceMappingURL=routes.storefront.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.storefront.d.ts","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAEzE;;;;;;;;GAQG;AACH,wBAAsB,gCAAgC,CACpD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE;IAAE,gBAAgB,EAAE,gBAAgB,CAAA;CAAE,GAC3C,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -0,0 +1,20 @@
1
+ import { getResolvedChannel } from '@endora-commerce/platform/kernel';
2
+ /**
3
+ * Public storefront read for the shop / company contact information.
4
+ *
5
+ * GET /api/v1/storefront/shop-info
6
+ *
7
+ * Resolves the `shop.*` settings for the request's sales channel (from the
8
+ * `x-sales-channel` header, falling back to the system-default channel). Every
9
+ * field is always present as a string; unset values come back empty.
10
+ */
11
+ export async function registerSettingsStorefrontRoutes(app, deps) {
12
+ app.get('/api/v1/storefront/shop-info', async (request) => {
13
+ // The id, not the code: the channel is already resolved, and handing the
14
+ // resolver a code made it re-resolve one (feature 075 / D-87).
15
+ const salesChannelId = getResolvedChannel(request).id;
16
+ const data = await deps.shopInfoResolver.resolve(salesChannelId);
17
+ return { data };
18
+ });
19
+ }
20
+ //# sourceMappingURL=routes.storefront.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.storefront.js","sourceRoot":"","sources":["../../src/backend/routes.storefront.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAGtE;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,GAAoB,EACpB,IAA4C;IAE5C,GAAG,CAAC,GAAG,CAAC,8BAA8B,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACxD,yEAAyE;QACzE,+DAA+D;QAC/D,MAAM,cAAc,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACjE,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,83 @@
1
+ import type { Redis } from 'ioredis';
2
+ import type { CacheNamespaceDto } from '@endora-commerce/contracts';
3
+ import { type InProcessCacheRegistry } from '@endora-commerce/platform/kernel';
4
+ /**
5
+ * Static registry of clearable cache namespaces. Each entry owns one or more
6
+ * key-glob patterns; clearing a namespace SCAN+DELs every matching key.
7
+ *
8
+ * Patterns mirror the prefixes each module's cache service writes with:
9
+ * - settings: `settings:v1:*` (SettingsCache)
10
+ * - sales_channels: `sales-channels:v2:*` (SalesChannelsCache)
11
+ * - blog: `blog:v1:*` (BlogCacheService)
12
+ * - cms: `cms:v1:*` (CmsCache)
13
+ * - megamenu: `megamenu:v1:*` (MegamenuCache)
14
+ * - dictionaries: `dictionary:*` (DictionaryCache)
15
+ * - carts: `b2b:cart:recompute:*` (CartRecomputeCache)
16
+ *
17
+ * The first two take their prefix from the cache itself rather than repeating
18
+ * the literal: `sales_channels` spent from feature 053 to now pointing at
19
+ * `sales-channels:v1:*` while the cache wrote `v2`, so the button cleared
20
+ * nothing at all for that namespace.
21
+ *
22
+ * Keep this list in sync when a new Redis-backed cache prefix is introduced.
23
+ */
24
+ interface CacheNamespace {
25
+ key: string;
26
+ label: string;
27
+ description: string;
28
+ patterns: string[];
29
+ }
30
+ export declare const CACHE_NAMESPACES: readonly CacheNamespace[];
31
+ export interface ClearedNamespace {
32
+ key: string;
33
+ deletedKeysCount: number;
34
+ }
35
+ /**
36
+ * Admin-facing cache maintenance. Lets an operator flush selected cache
37
+ * namespaces so freshly-published content / changed settings show up without
38
+ * waiting for TTL expiry.
39
+ *
40
+ * Six of the eight namespaces live only in Redis, so SCAN+DEL over their key
41
+ * patterns is a complete clear. Two — `settings` and `sales_channels` — keep a
42
+ * per-process layer in front of Redis, and for those the clear goes through
43
+ * the cache that owns both layers (see {@link InProcessCacheRegistry}), which
44
+ * is also the only way to get the ordering right: shared layer first, prefix
45
+ * marked for the whole operation.
46
+ *
47
+ * The cost of that ordering is worth naming. Immediately after a clear both
48
+ * layers are cold and the marked prefix sends reads through to PostgreSQL, so
49
+ * a platform-wide burst of setting reads lands on the database. The queries are
50
+ * trivial (small table, unique index on `code`), but the connection pool is
51
+ * not: it is the scarce resource. That is acceptable for a rare,
52
+ * operator-initiated action and unacceptable in a loop, which is why the route
53
+ * carries its own rate limit (`routes.cache.ts`).
54
+ */
55
+ export declare class CacheAdminService {
56
+ private readonly redis?;
57
+ /** Injectable so a test does not clear this process's real caches. */
58
+ private readonly caches;
59
+ constructor(redis?: Redis | undefined,
60
+ /** Injectable so a test does not clear this process's real caches. */
61
+ caches?: InProcessCacheRegistry);
62
+ get enabled(): boolean;
63
+ listNamespaces(): CacheNamespaceDto[];
64
+ /**
65
+ * Clear the given namespaces (or every namespace when passed `'all'`).
66
+ * Unknown keys are ignored. Returns the per-namespace deleted-key counts.
67
+ */
68
+ clear(namespaces: 'all' | string[]): Promise<{
69
+ cleared: ClearedNamespace[];
70
+ totalDeletedKeys: number;
71
+ }>;
72
+ /**
73
+ * Prefer the owning cache when this process has one: it drops the shared
74
+ * layer and its own in-memory layer as one marked operation. A process that
75
+ * registered none — the `cache:clear` CLI, a worker without the module —
76
+ * has no in-memory layer to be stale, so clearing the Redis keys is the
77
+ * whole job.
78
+ */
79
+ private clearNamespace;
80
+ private scanDelete;
81
+ }
82
+ export {};
83
+ //# sourceMappingURL=cache-admin.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-admin.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/cache-admin.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,kCAAkC,CAAC;AAU1C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,eAAO,MAAM,gBAAgB,EAAE,SAAS,cAAc,EAiD5C,CAAC;AAEX,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvB,sEAAsE;IACtE,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,KAAK,CAAC,EAAE,KAAK,YAAA;IAC9B,sEAAsE;IACrD,MAAM,GAAE,sBAAwC;IAGnE,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,cAAc,IAAI,iBAAiB,EAAE;IAQrC;;;OAGG;IACG,KAAK,CACT,UAAU,EAAE,KAAK,GAAG,MAAM,EAAE,GAC3B,OAAO,CAAC;QAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC;IAgBrE;;;;;;OAMG;YACW,cAAc;YAYd,UAAU;CAgBzB"}