@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.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/admin/api/settings-client.d.ts +47 -0
- package/dist/admin/api/settings-client.d.ts.map +1 -0
- package/dist/admin/api/settings-client.js +41 -0
- package/dist/admin/api/settings-client.js.map +1 -0
- package/dist/admin/components/ActivationPointerRow.d.ts +21 -0
- package/dist/admin/components/ActivationPointerRow.d.ts.map +1 -0
- package/dist/admin/components/ActivationPointerRow.js +28 -0
- package/dist/admin/components/ActivationPointerRow.js.map +1 -0
- package/dist/admin/components/AssetIdSettingInput.d.ts +12 -0
- package/dist/admin/components/AssetIdSettingInput.d.ts.map +1 -0
- package/dist/admin/components/AssetIdSettingInput.js +10 -0
- package/dist/admin/components/AssetIdSettingInput.js.map +1 -0
- package/dist/admin/components/ConfigurationReferenceInput.d.ts +39 -0
- package/dist/admin/components/ConfigurationReferenceInput.d.ts.map +1 -0
- package/dist/admin/components/ConfigurationReferenceInput.js +74 -0
- package/dist/admin/components/ConfigurationReferenceInput.js.map +1 -0
- package/dist/admin/components/ConflictBanner.d.ts +13 -0
- package/dist/admin/components/ConflictBanner.d.ts.map +1 -0
- package/dist/admin/components/ConflictBanner.js +12 -0
- package/dist/admin/components/ConflictBanner.js.map +1 -0
- package/dist/admin/components/ImageSettingInput.d.ts +34 -0
- package/dist/admin/components/ImageSettingInput.d.ts.map +1 -0
- package/dist/admin/components/ImageSettingInput.js +36 -0
- package/dist/admin/components/ImageSettingInput.js.map +1 -0
- package/dist/admin/components/QuoteRequestsSettingsTab.d.ts +3 -0
- package/dist/admin/components/QuoteRequestsSettingsTab.d.ts.map +1 -0
- package/dist/admin/components/QuoteRequestsSettingsTab.js +69 -0
- package/dist/admin/components/QuoteRequestsSettingsTab.js.map +1 -0
- package/dist/admin/components/SellerCompanyDataInput.d.ts +22 -0
- package/dist/admin/components/SellerCompanyDataInput.d.ts.map +1 -0
- package/dist/admin/components/SellerCompanyDataInput.js +53 -0
- package/dist/admin/components/SellerCompanyDataInput.js.map +1 -0
- package/dist/admin/components/SettingRowEditor.d.ts +55 -0
- package/dist/admin/components/SettingRowEditor.d.ts.map +1 -0
- package/dist/admin/components/SettingRowEditor.js +160 -0
- package/dist/admin/components/SettingRowEditor.js.map +1 -0
- package/dist/admin/index.d.ts +39 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +87 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/CachePage.d.ts +14 -0
- package/dist/admin/pages/CachePage.d.ts.map +1 -0
- package/dist/admin/pages/CachePage.js +124 -0
- package/dist/admin/pages/CachePage.js.map +1 -0
- package/dist/admin/pages/GroupsPage.d.ts +17 -0
- package/dist/admin/pages/GroupsPage.d.ts.map +1 -0
- package/dist/admin/pages/GroupsPage.js +84 -0
- package/dist/admin/pages/GroupsPage.js.map +1 -0
- package/dist/admin/pages/SettingsPage.d.ts +20 -0
- package/dist/admin/pages/SettingsPage.d.ts.map +1 -0
- package/dist/admin/pages/SettingsPage.js +453 -0
- package/dist/admin/pages/SettingsPage.js.map +1 -0
- package/dist/backend/cli/cache-clear.d.ts +23 -0
- package/dist/backend/cli/cache-clear.d.ts.map +1 -0
- package/dist/backend/cli/cache-clear.js +37 -0
- package/dist/backend/cli/cache-clear.js.map +1 -0
- package/dist/backend/index.d.ts +100 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +125 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +308 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +188 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.cache.d.ts +20 -0
- package/dist/backend/routes.cache.d.ts.map +1 -0
- package/dist/backend/routes.cache.js +22 -0
- package/dist/backend/routes.cache.js.map +1 -0
- package/dist/backend/routes.homepage.d.ts +15 -0
- package/dist/backend/routes.homepage.d.ts.map +1 -0
- package/dist/backend/routes.homepage.js +20 -0
- package/dist/backend/routes.homepage.js.map +1 -0
- package/dist/backend/routes.product-card-buttons.d.ts +15 -0
- package/dist/backend/routes.product-card-buttons.d.ts.map +1 -0
- package/dist/backend/routes.product-card-buttons.js +21 -0
- package/dist/backend/routes.product-card-buttons.js.map +1 -0
- package/dist/backend/routes.speculation-rules.d.ts +16 -0
- package/dist/backend/routes.speculation-rules.d.ts.map +1 -0
- package/dist/backend/routes.speculation-rules.js +21 -0
- package/dist/backend/routes.speculation-rules.js.map +1 -0
- package/dist/backend/routes.storefront.d.ts +15 -0
- package/dist/backend/routes.storefront.d.ts.map +1 -0
- package/dist/backend/routes.storefront.js +20 -0
- package/dist/backend/routes.storefront.js.map +1 -0
- package/dist/backend/services/cache-admin.service.d.ts +83 -0
- package/dist/backend/services/cache-admin.service.d.ts.map +1 -0
- package/dist/backend/services/cache-admin.service.js +148 -0
- package/dist/backend/services/cache-admin.service.js.map +1 -0
- package/dist/backend/services/homepage-resolver.d.ts +25 -0
- package/dist/backend/services/homepage-resolver.d.ts.map +1 -0
- package/dist/backend/services/homepage-resolver.js +31 -0
- package/dist/backend/services/homepage-resolver.js.map +1 -0
- package/dist/backend/services/product-card-buttons-resolver.d.ts +25 -0
- package/dist/backend/services/product-card-buttons-resolver.d.ts.map +1 -0
- package/dist/backend/services/product-card-buttons-resolver.js +37 -0
- package/dist/backend/services/product-card-buttons-resolver.js.map +1 -0
- package/dist/backend/services/registered-settings-manifests.d.ts +45 -0
- package/dist/backend/services/registered-settings-manifests.d.ts.map +1 -0
- package/dist/backend/services/registered-settings-manifests.js +55 -0
- package/dist/backend/services/registered-settings-manifests.js.map +1 -0
- package/dist/backend/services/setting-write-validators.d.ts +43 -0
- package/dist/backend/services/setting-write-validators.d.ts.map +1 -0
- package/dist/backend/services/setting-write-validators.js +43 -0
- package/dist/backend/services/setting-write-validators.js.map +1 -0
- package/dist/backend/services/settings-admin.service.d.ts +206 -0
- package/dist/backend/services/settings-admin.service.d.ts.map +1 -0
- package/dist/backend/services/settings-admin.service.js +642 -0
- package/dist/backend/services/settings-admin.service.js.map +1 -0
- package/dist/backend/services/shop-info-resolver.d.ts +15 -0
- package/dist/backend/services/shop-info-resolver.d.ts.map +1 -0
- package/dist/backend/services/shop-info-resolver.js +44 -0
- package/dist/backend/services/shop-info-resolver.js.map +1 -0
- package/dist/backend/services/speculation-rules-resolver.d.ts +28 -0
- package/dist/backend/services/speculation-rules-resolver.d.ts.map +1 -0
- package/dist/backend/services/speculation-rules-resolver.js +39 -0
- package/dist/backend/services/speculation-rules-resolver.js.map +1 -0
- package/dist/manifest.d.ts +194 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +379 -0
- package/dist/manifest.js.map +1 -0
- package/docs/settings/index.md +171 -0
- package/i18n/en.json +167 -0
- package/i18n/pl.json +167 -0
- package/package.json +96 -0
- 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"}
|