@adminide-stack/marketplace-module-server 12.0.4-alpha.92 → 12.0.4-alpha.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +321 -0
- package/lib/constants/extension-events.d.ts +136 -0
- package/lib/constants/extension-events.d.ts.map +1 -0
- package/lib/containers/module.d.ts +8 -0
- package/lib/containers/module.d.ts.map +1 -1
- package/lib/containers/module.js +9 -3
- package/lib/containers/module.js.map +1 -1
- package/lib/dataloaders/index.d.ts +2 -0
- package/lib/dataloaders/index.d.ts.map +1 -0
- package/lib/dataloaders/registry-extension-data-loader.d.ts +6 -0
- package/lib/dataloaders/registry-extension-data-loader.d.ts.map +1 -0
- package/lib/dataloaders/registry-extension-data-loader.js +6 -0
- package/lib/dataloaders/registry-extension-data-loader.js.map +1 -0
- package/lib/graphql/resolvers/index.d.ts +1 -1
- package/lib/graphql/resolvers/index.d.ts.map +1 -1
- package/lib/graphql/resolvers/index.js +1 -0
- package/lib/graphql/resolvers/index.js.map +1 -0
- package/lib/graphql/resolvers/installed-extension-resolver.d.ts +5 -0
- package/lib/graphql/resolvers/installed-extension-resolver.d.ts.map +1 -0
- package/lib/graphql/resolvers/installed-extension-resolver.js +183 -0
- package/lib/graphql/resolvers/installed-extension-resolver.js.map +1 -0
- package/lib/graphql/resolvers/registry-extension-resolver.d.ts +5 -0
- package/lib/graphql/resolvers/registry-extension-resolver.d.ts.map +1 -0
- package/lib/graphql/resolvers/{resolvers.js → registry-extension-resolver.js} +59 -47
- package/lib/graphql/resolvers/registry-extension-resolver.js.map +1 -0
- package/lib/graphql/schemas/extension-registry.graphql +74 -0
- package/lib/graphql/schemas/extension-registry.graphql.js +1 -0
- package/lib/graphql/schemas/extension-registry.graphql.js.map +1 -0
- package/lib/graphql/schemas/index.d.ts.map +1 -1
- package/lib/graphql/schemas/index.js +1 -4
- package/lib/graphql/schemas/index.js.map +1 -1
- package/lib/graphql/schemas/installed-extension.graphql +294 -0
- package/lib/graphql/schemas/installed-extension.graphql.js +1 -0
- package/lib/graphql/schemas/installed-extension.graphql.js.map +1 -0
- package/lib/graphql/schemas/service.graphql +181 -0
- package/lib/graphql/schemas/service.graphql.js +1 -0
- package/lib/graphql/schemas/service.graphql.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/module.d.ts.map +1 -1
- package/lib/module.js +6 -4
- package/lib/module.js.map +1 -1
- package/lib/plugins/extension-moleculer-service.d.ts +86 -0
- package/lib/plugins/extension-moleculer-service.d.ts.map +1 -0
- package/lib/plugins/index.d.ts +2 -0
- package/lib/plugins/index.d.ts.map +1 -0
- package/lib/services/extension-service.d.ts +35 -47
- package/lib/services/extension-service.d.ts.map +1 -1
- package/lib/services/extension-service.js +33 -15
- package/lib/services/extension-service.js.map +1 -1
- package/lib/services/index.d.ts +2 -0
- package/lib/services/index.d.ts.map +1 -1
- package/lib/services/installed-extension-service-ext.d.ts +13 -0
- package/lib/services/installed-extension-service-ext.d.ts.map +1 -0
- package/lib/services/installed-extension-service-ext.js +271 -0
- package/lib/services/installed-extension-service-ext.js.map +1 -0
- package/lib/services/installed-extension-service.d.ts +93 -0
- package/lib/services/installed-extension-service.d.ts.map +1 -0
- package/lib/services/installed-extension-service.js +400 -0
- package/lib/services/installed-extension-service.js.map +1 -0
- package/lib/services/installed-extension-service.test.d.ts +2 -0
- package/lib/services/installed-extension-service.test.d.ts.map +1 -0
- package/lib/store/index.d.ts +1 -1
- package/lib/store/index.d.ts.map +1 -1
- package/lib/store/models/index.d.ts +1 -0
- package/lib/store/models/index.d.ts.map +1 -1
- package/lib/store/models/installed-extension-model.d.ts +4 -0
- package/lib/store/models/installed-extension-model.d.ts.map +1 -0
- package/lib/store/models/installed-extension-model.js +297 -0
- package/lib/store/models/installed-extension-model.js.map +1 -0
- package/lib/store/models/registry-extension-model.d.ts +2 -7
- package/lib/store/models/registry-extension-model.d.ts.map +1 -1
- package/lib/store/models/registry-extension-model.js +40 -19
- package/lib/store/models/registry-extension-model.js.map +1 -1
- package/lib/store/repositories/index.d.ts +3 -0
- package/lib/store/repositories/index.d.ts.map +1 -0
- package/lib/store/repositories/installed-extension-repository.d.ts +71 -0
- package/lib/store/repositories/installed-extension-repository.d.ts.map +1 -0
- package/lib/store/repositories/installed-extension-repository.js +414 -0
- package/lib/store/repositories/installed-extension-repository.js.map +1 -0
- package/lib/store/repositories/registry-extension-repository.d.ts +54 -0
- package/lib/store/repositories/registry-extension-repository.d.ts.map +1 -0
- package/lib/store/{repository → repositories}/registry-extension-repository.js +44 -42
- package/lib/store/repositories/registry-extension-repository.js.map +1 -0
- package/lib/templates/constants/DB_COLL_NAMES.ts.template +5 -0
- package/lib/templates/constants/SERVER_TYPES.ts.template +3 -0
- package/lib/templates/repositories/InstalledExtensionRepository.ts.template +94 -0
- package/lib/templates/repositories/RegistryExtensionRepository.ts.template +10 -15
- package/lib/templates/services/InstalledExtensionDataLoader.ts.template +2 -0
- package/lib/templates/services/InstalledExtensionService.ts.template +168 -0
- package/lib/templates/services/RegistryExtensionDataLoader.ts.template +2 -0
- package/lib/templates/services/RegistryExtensionService.ts.template +10 -10
- package/lib/tests/extension-integration.test.d.ts +2 -0
- package/lib/tests/extension-integration.test.d.ts.map +1 -0
- package/lib/tests/test-extension-services.d.ts +1 -0
- package/lib/tests/test-extension-services.d.ts.map +1 -0
- package/package.json +12 -7
- package/lib/graphql/resolvers/resolvers.d.ts +0 -2
- package/lib/graphql/resolvers/resolvers.d.ts.map +0 -1
- package/lib/graphql/resolvers/resolvers.js.map +0 -1
- package/lib/graphql/schemas/extension.graphql +0 -57
- package/lib/graphql/schemas/extension.graphql.js +0 -1
- package/lib/graphql/schemas/extension.graphql.js.map +0 -1
- package/lib/store/repository/index.d.ts +0 -2
- package/lib/store/repository/index.d.ts.map +0 -1
- package/lib/store/repository/registry-extension-repository.d.ts +0 -31
- package/lib/store/repository/registry-extension-repository.d.ts.map +0 -1
- package/lib/store/repository/registry-extension-repository.js.map +0 -1
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import {__decorate,__param,__metadata}from'tslib';import {injectable,inject}from'inversify';import'@cdm-logger/core';import {ServiceBroker}from'moleculer';import {CommonType}from'@common-stack/core';import {Emitter,DisposableCollection}from'@adminide-stack/core';import {SERVER_TYPES,ExtensionActivationState}from'common/server';import {BaseService2}from'@common-stack/store-mongo';var InstalledExtensionService_1;
|
|
2
|
+
let InstalledExtensionService = InstalledExtensionService_1 = class InstalledExtensionService extends BaseService2 {
|
|
3
|
+
installedExtensionRepository;
|
|
4
|
+
registryExtensionService;
|
|
5
|
+
broker;
|
|
6
|
+
onExtensionInstalled = new Emitter();
|
|
7
|
+
onExtensionUninstalled = new Emitter();
|
|
8
|
+
onExtensionEnabled = new Emitter();
|
|
9
|
+
onExtensionDisabled = new Emitter();
|
|
10
|
+
onExtensionUpdated = new Emitter();
|
|
11
|
+
onExtensionVersionUpdated = new Emitter();
|
|
12
|
+
onExtensionConfigurationUpdated = new Emitter();
|
|
13
|
+
onExtensionSyncCompleted = new Emitter();
|
|
14
|
+
onExtensionCleanupCompleted = new Emitter();
|
|
15
|
+
onExtensionStatusChanged = new Emitter();
|
|
16
|
+
onExtensionActivationFailed = new Emitter();
|
|
17
|
+
onExtensionDeprecated = new Emitter();
|
|
18
|
+
onExtensionOrphaned = new Emitter();
|
|
19
|
+
toDispose = new DisposableCollection(this.onExtensionInstalled, this.onExtensionUninstalled, this.onExtensionEnabled, this.onExtensionDisabled, this.onExtensionUpdated, this.onExtensionVersionUpdated, this.onExtensionConfigurationUpdated, this.onExtensionSyncCompleted, this.onExtensionCleanupCompleted, this.onExtensionStatusChanged, this.onExtensionActivationFailed, this.onExtensionDeprecated, this.onExtensionOrphaned);
|
|
20
|
+
logger;
|
|
21
|
+
constructor(installedExtensionRepository, registryExtensionService, broker, logger) {
|
|
22
|
+
super(installedExtensionRepository);
|
|
23
|
+
this.installedExtensionRepository = installedExtensionRepository;
|
|
24
|
+
this.registryExtensionService = registryExtensionService;
|
|
25
|
+
this.broker = broker;
|
|
26
|
+
this.logger = logger.child({
|
|
27
|
+
className: InstalledExtensionService_1.name
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
dispose() {
|
|
31
|
+
this.toDispose.dispose();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Install a new extension from the registry
|
|
35
|
+
*/
|
|
36
|
+
async installExtension(input) {
|
|
37
|
+
this.logger.info(`Installing extension ${input.extensionSlug} for tenant ${input.tenantId}`);
|
|
38
|
+
// Check if extension already exists
|
|
39
|
+
const existing = await this.installedExtensionRepository.exists(input.tenantId, input.extensionSlug);
|
|
40
|
+
if (existing) {
|
|
41
|
+
throw new Error(`Extension ${input.extensionSlug} is already installed for tenant ${input.tenantId}`);
|
|
42
|
+
}
|
|
43
|
+
// Get extension from registry to validate
|
|
44
|
+
const registryExtension = await this.registryExtensionService.findExtension(input.extensionSlug);
|
|
45
|
+
if (!registryExtension) {
|
|
46
|
+
throw new Error(`Extension ${input.extensionSlug} not found in registry`);
|
|
47
|
+
}
|
|
48
|
+
// Delegate to the internal install method (validation already done)
|
|
49
|
+
return this.doInstallExtension(input, registryExtension);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Internal method to perform the actual installation after validation
|
|
53
|
+
*/
|
|
54
|
+
async doInstallExtension(input, registryExtension) {
|
|
55
|
+
// Create the installed extension record with proper defaults
|
|
56
|
+
const installedExtension = await this.installedExtensionRepository.create({
|
|
57
|
+
tenantId: input.tenantId,
|
|
58
|
+
extensionSlug: input.extensionSlug,
|
|
59
|
+
extension: registryExtension.id,
|
|
60
|
+
version: input.version || registryExtension.version,
|
|
61
|
+
installedVersion: registryExtension.version,
|
|
62
|
+
installedBy: input.installedBy,
|
|
63
|
+
policies: {
|
|
64
|
+
allowOrphanedExecution: false,
|
|
65
|
+
requireSecurityUpdates: true,
|
|
66
|
+
autoRemoveDeprecated: false,
|
|
67
|
+
deprecationGracePeriod: 30,
|
|
68
|
+
...input.policies
|
|
69
|
+
},
|
|
70
|
+
settings: {
|
|
71
|
+
userEnabled: true,
|
|
72
|
+
systemEnabled: true,
|
|
73
|
+
...input.settings
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
// Fire installation event
|
|
77
|
+
const event = {
|
|
78
|
+
extensionSlug: input.extensionSlug,
|
|
79
|
+
tenantId: input.tenantId,
|
|
80
|
+
installedBy: input.installedBy,
|
|
81
|
+
installedVersion: installedExtension.installedVersion,
|
|
82
|
+
registryRef: registryExtension.id,
|
|
83
|
+
installedAt: new Date().toISOString(),
|
|
84
|
+
policies: input.policies,
|
|
85
|
+
settings: input.settings
|
|
86
|
+
};
|
|
87
|
+
this.onExtensionInstalled.fire(event);
|
|
88
|
+
this.logger.info(`Successfully installed extension ${input.extensionSlug}`);
|
|
89
|
+
return installedExtension;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Uninstall an extension
|
|
93
|
+
*/
|
|
94
|
+
async uninstallExtension(tenantId, extensionSlug, uninstalledBy) {
|
|
95
|
+
this.logger.info(`Uninstalling extension ${extensionSlug} for tenant ${tenantId}`);
|
|
96
|
+
const extension = await this.installedExtensionRepository.findByTenantAndId(tenantId, extensionSlug);
|
|
97
|
+
if (!extension) {
|
|
98
|
+
throw new Error(`Extension ${extensionSlug} not found for tenant ${tenantId}`);
|
|
99
|
+
}
|
|
100
|
+
// Check dependencies - prevent uninstalling if other extensions depend on this
|
|
101
|
+
const dependencyGraph = await this.installedExtensionRepository.getDependencyGraph(tenantId);
|
|
102
|
+
const extensionDeps = dependencyGraph.dependencies.find(dep => dep.extensionSlug === extensionSlug);
|
|
103
|
+
if (extensionDeps && extensionDeps.dependents.length > 0) {
|
|
104
|
+
throw new Error(`Cannot uninstall ${extensionSlug}: Required by ${extensionDeps.dependents.join(', ')}`);
|
|
105
|
+
}
|
|
106
|
+
// Delete the extension
|
|
107
|
+
const deleted = await this.installedExtensionRepository.deleteExtension(tenantId, extensionSlug);
|
|
108
|
+
if (deleted) {
|
|
109
|
+
// Fire uninstallation event
|
|
110
|
+
const event = {
|
|
111
|
+
extensionSlug,
|
|
112
|
+
tenantId,
|
|
113
|
+
uninstalledBy,
|
|
114
|
+
uninstalledAt: new Date().toISOString(),
|
|
115
|
+
wasEnabled: extension.settings?.userEnabled || false,
|
|
116
|
+
dependencies: extension.dependencies || []
|
|
117
|
+
};
|
|
118
|
+
this.onExtensionUninstalled.fire(event);
|
|
119
|
+
this.logger.info(`Successfully uninstalled extension ${extensionSlug}`);
|
|
120
|
+
}
|
|
121
|
+
return deleted;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Update an installed extension's configuration or status
|
|
125
|
+
*/
|
|
126
|
+
async updateInstalledExtension(tenantId, extensionSlug, update) {
|
|
127
|
+
this.logger.info(`Updating installed extension ${extensionSlug} for tenant ${tenantId}`);
|
|
128
|
+
const updatedExtension = await this.installedExtensionRepository.updateExtension(tenantId, extensionSlug, {
|
|
129
|
+
...update,
|
|
130
|
+
lastUpdatedBy: update.lastUpdatedBy
|
|
131
|
+
});
|
|
132
|
+
if (!updatedExtension) {
|
|
133
|
+
throw new Error(`Extension ${extensionSlug} not found for tenant ${tenantId}`);
|
|
134
|
+
}
|
|
135
|
+
// Fire update event if status changed
|
|
136
|
+
if (update.status || update.settings) {
|
|
137
|
+
const event = {
|
|
138
|
+
extensionSlug,
|
|
139
|
+
tenantId,
|
|
140
|
+
updatedBy: update.lastUpdatedBy || 'system',
|
|
141
|
+
updatedAt: new Date().toISOString(),
|
|
142
|
+
changes: update
|
|
143
|
+
};
|
|
144
|
+
this.onExtensionUpdated.fire(event);
|
|
145
|
+
}
|
|
146
|
+
return updatedExtension;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get a specific installed extension
|
|
150
|
+
*/
|
|
151
|
+
async getInstalledExtension(tenantId, extensionSlug) {
|
|
152
|
+
const result = await this.installedExtensionRepository.findByTenantAndId(tenantId, extensionSlug);
|
|
153
|
+
return result ? result : null;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get all installed extensions for a tenant with optional filtering
|
|
157
|
+
*/
|
|
158
|
+
async getInstalledExtensions(filter) {
|
|
159
|
+
const results = await this.installedExtensionRepository.findExtensions(filter);
|
|
160
|
+
return results;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Enable/disable an installed extension
|
|
164
|
+
*/
|
|
165
|
+
async toggleExtension(tenantId, extensionSlug, enabled, toggledBy) {
|
|
166
|
+
const activationState = enabled ? ExtensionActivationState.Enabled : ExtensionActivationState.DisabledByUser;
|
|
167
|
+
const updatedExtension = await this.updateInstalledExtension(tenantId, extensionSlug, {
|
|
168
|
+
settings: {
|
|
169
|
+
userEnabled: enabled
|
|
170
|
+
},
|
|
171
|
+
runtime: {
|
|
172
|
+
activationState,
|
|
173
|
+
...(enabled ? {
|
|
174
|
+
lastActivated: new Date()
|
|
175
|
+
} : {
|
|
176
|
+
lastDeactivated: new Date()
|
|
177
|
+
})
|
|
178
|
+
},
|
|
179
|
+
lastUpdatedBy: toggledBy
|
|
180
|
+
});
|
|
181
|
+
// Fire toggle event
|
|
182
|
+
const event = enabled ? {
|
|
183
|
+
extensionSlug,
|
|
184
|
+
tenantId,
|
|
185
|
+
enabledBy: toggledBy,
|
|
186
|
+
enabledAt: new Date().toISOString(),
|
|
187
|
+
previousState: updatedExtension.runtime.activationState
|
|
188
|
+
} : {
|
|
189
|
+
extensionSlug,
|
|
190
|
+
tenantId,
|
|
191
|
+
disabledBy: toggledBy,
|
|
192
|
+
disabledAt: new Date().toISOString(),
|
|
193
|
+
reason: 'user'
|
|
194
|
+
};
|
|
195
|
+
// Fire status change event
|
|
196
|
+
if (enabled) {
|
|
197
|
+
this.onExtensionEnabled.fire(event);
|
|
198
|
+
} else {
|
|
199
|
+
this.onExtensionDisabled.fire(event);
|
|
200
|
+
}
|
|
201
|
+
return updatedExtension;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Update extension settings/configuration
|
|
205
|
+
*/
|
|
206
|
+
async updateExtensionSettings(tenantId, extensionSlug, userConfig, updatedBy) {
|
|
207
|
+
const updatedExtension = await this.updateInstalledExtension(tenantId, extensionSlug, {
|
|
208
|
+
settings: {
|
|
209
|
+
userConfig
|
|
210
|
+
},
|
|
211
|
+
lastUpdatedBy: updatedBy
|
|
212
|
+
});
|
|
213
|
+
// Fire configuration update event
|
|
214
|
+
const event = {
|
|
215
|
+
extensionSlug,
|
|
216
|
+
tenantId,
|
|
217
|
+
updatedBy,
|
|
218
|
+
updatedAt: new Date().toISOString(),
|
|
219
|
+
configType: 'user',
|
|
220
|
+
newConfig: userConfig,
|
|
221
|
+
previousConfig: updatedExtension.settings.userConfig
|
|
222
|
+
};
|
|
223
|
+
// Fire configuration update event
|
|
224
|
+
this.onExtensionConfigurationUpdated.fire(event);
|
|
225
|
+
return updatedExtension;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Check for extension updates and return available updates
|
|
229
|
+
*/
|
|
230
|
+
async checkForUpdates(tenantId, extensionSlug) {
|
|
231
|
+
const query = {
|
|
232
|
+
tenantId
|
|
233
|
+
};
|
|
234
|
+
if (extensionSlug) {
|
|
235
|
+
query.extensionSlug = extensionSlug;
|
|
236
|
+
}
|
|
237
|
+
const installedExtensions = await this.installedExtensionRepository.findExtensions(query);
|
|
238
|
+
const updates = [];
|
|
239
|
+
for (const installed of installedExtensions) {
|
|
240
|
+
const registryExtension = await this.registryExtensionService.findExtension(installed.extension?.toString());
|
|
241
|
+
if (registryExtension && registryExtension.version !== installed.installedVersion) {
|
|
242
|
+
updates.push({
|
|
243
|
+
extensionSlug: installed.extension?.toString(),
|
|
244
|
+
currentVersion: installed.installedVersion,
|
|
245
|
+
availableVersion: registryExtension.version,
|
|
246
|
+
isSecurityUpdate: this.isSecurityUpdate(installed.installedVersion, registryExtension.version)
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return updates;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Update an extension to a newer version
|
|
254
|
+
*/
|
|
255
|
+
async updateExtensionVersion(tenantId, extensionSlug, targetVersion, updatedBy) {
|
|
256
|
+
const extension = await this.getInstalledExtension(tenantId, extensionSlug);
|
|
257
|
+
if (!extension) {
|
|
258
|
+
throw new Error(`Extension ${extensionSlug} not found for tenant ${tenantId}`);
|
|
259
|
+
}
|
|
260
|
+
// Validate target version exists in registry
|
|
261
|
+
const registryExtension = await this.registryExtensionService.findExtension(extensionSlug);
|
|
262
|
+
if (!registryExtension) {
|
|
263
|
+
throw new Error(`Extension ${extensionSlug} not found in registry`);
|
|
264
|
+
}
|
|
265
|
+
const updatedExtension = await this.updateInstalledExtension(tenantId, extensionSlug, {
|
|
266
|
+
lastUpdatedBy: updatedBy
|
|
267
|
+
// Note: Version update should be handled at the model level, not through update input
|
|
268
|
+
});
|
|
269
|
+
// Fire version update event
|
|
270
|
+
const event = {
|
|
271
|
+
extensionSlug,
|
|
272
|
+
tenantId,
|
|
273
|
+
updatedBy,
|
|
274
|
+
fromVersion: extension.installedVersion,
|
|
275
|
+
toVersion: targetVersion,
|
|
276
|
+
updatedAt: new Date().toISOString(),
|
|
277
|
+
isSecurityUpdate: this.isSecurityUpdate(extension.installedVersion, targetVersion)
|
|
278
|
+
};
|
|
279
|
+
// Fire version update event
|
|
280
|
+
this.onExtensionVersionUpdated.fire(event);
|
|
281
|
+
return updatedExtension;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Sync installed extensions with registry status
|
|
285
|
+
*/
|
|
286
|
+
async syncWithRegistry(tenantId) {
|
|
287
|
+
this.logger.info(`Syncing installed extensions with registry for tenant ${tenantId}`);
|
|
288
|
+
const installedExtensions = await this.installedExtensionRepository.findExtensions({
|
|
289
|
+
tenantId
|
|
290
|
+
});
|
|
291
|
+
const syncUpdates = [];
|
|
292
|
+
for (const installed of installedExtensions) {
|
|
293
|
+
const registryExtension = await this.registryExtensionService.findExtension(installed.extension?.toString());
|
|
294
|
+
if (!registryExtension) {
|
|
295
|
+
// Extension removed from registry
|
|
296
|
+
syncUpdates.push({
|
|
297
|
+
tenantId,
|
|
298
|
+
extensionSlug: installed.extension?.toString(),
|
|
299
|
+
registryStatus: 'removed',
|
|
300
|
+
isOrphaned: true
|
|
301
|
+
});
|
|
302
|
+
} else {
|
|
303
|
+
syncUpdates.push({
|
|
304
|
+
tenantId,
|
|
305
|
+
extensionSlug: installed.extension?.toString(),
|
|
306
|
+
registryStatus: registryExtension.status || 'active',
|
|
307
|
+
latestVersion: registryExtension.version,
|
|
308
|
+
isOrphaned: false
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
await this.installedExtensionRepository.syncRegistryStatus(syncUpdates);
|
|
313
|
+
// Fire sync completion event
|
|
314
|
+
const orphanedUpdates = syncUpdates.filter(u => u.isOrphaned);
|
|
315
|
+
syncUpdates.filter(u => u.registryStatus === 'deprecated');
|
|
316
|
+
const versionsUpdates = syncUpdates.filter(u => u.latestVersion);
|
|
317
|
+
const event = {
|
|
318
|
+
tenantId,
|
|
319
|
+
syncedAt: new Date().toISOString(),
|
|
320
|
+
extensionsChecked: syncUpdates.length,
|
|
321
|
+
extensionsOrphaned: orphanedUpdates.length,
|
|
322
|
+
extensionsUpdated: versionsUpdates.length,
|
|
323
|
+
errors: [] // No errors in this implementation
|
|
324
|
+
};
|
|
325
|
+
// Fire sync completed event
|
|
326
|
+
this.onExtensionSyncCompleted.fire(event);
|
|
327
|
+
this.logger.info(`Synced ${syncUpdates.length} extensions with registry`);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Get extensions that need attention
|
|
331
|
+
*/
|
|
332
|
+
async getExtensionsNeedingAttention(tenantId) {
|
|
333
|
+
const extensions = await this.getInstalledExtensions({
|
|
334
|
+
tenantId
|
|
335
|
+
});
|
|
336
|
+
const filtered = extensions.filter(ext => {
|
|
337
|
+
// Check for conditions that need attention
|
|
338
|
+
const isOrphaned = ext.lifecycle?.isOrphaned;
|
|
339
|
+
const isDeprecated = ext.lifecycle?.registryStatus === 'deprecated';
|
|
340
|
+
const hasUpdates = ext.availableVersion && ext.availableVersion !== ext.installedVersion;
|
|
341
|
+
return isOrphaned || isDeprecated || hasUpdates;
|
|
342
|
+
});
|
|
343
|
+
return filtered;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Clean up deprecated or removed extensions based on policies
|
|
347
|
+
*/
|
|
348
|
+
async cleanupExtensions(tenantId, dryRun = false) {
|
|
349
|
+
const result = {
|
|
350
|
+
removed: [],
|
|
351
|
+
deprecated: [],
|
|
352
|
+
warnings: []
|
|
353
|
+
};
|
|
354
|
+
const extensions = await this.installedExtensionRepository.findExtensions({
|
|
355
|
+
tenantId
|
|
356
|
+
});
|
|
357
|
+
for (const extension of extensions) {
|
|
358
|
+
const gracePeriodExpired = extension.policies.deprecationGracePeriod > 0 && extension.lifecycle.lastRegistryCheck && Date.now() - new Date(extension.lifecycle.lastRegistryCheck).getTime() > extension.policies.deprecationGracePeriod * 24 * 60 * 60 * 1000;
|
|
359
|
+
// Remove orphaned extensions
|
|
360
|
+
if (extension.lifecycle.isOrphaned && !extension.policies.allowOrphanedExecution) {
|
|
361
|
+
if (!dryRun) {
|
|
362
|
+
await this.uninstallExtension(tenantId, extension.extension?.toString(), 'system-cleanup');
|
|
363
|
+
}
|
|
364
|
+
result.removed.push(extension.extension?.toString());
|
|
365
|
+
}
|
|
366
|
+
// Remove deprecated extensions if auto-remove is enabled and grace period expired
|
|
367
|
+
else if (extension.lifecycle.registryStatus === 'deprecated' && extension.policies.autoRemoveDeprecated && gracePeriodExpired) {
|
|
368
|
+
if (!dryRun) {
|
|
369
|
+
await this.uninstallExtension(tenantId, extension.extension?.toString(), 'system-cleanup');
|
|
370
|
+
}
|
|
371
|
+
result.deprecated.push(extension.extension?.toString());
|
|
372
|
+
}
|
|
373
|
+
// Add warnings for extensions that need attention
|
|
374
|
+
else if (extension.lifecycle.registryStatus === 'deprecated' || extension.lifecycle.isOrphaned) {
|
|
375
|
+
result.warnings.push(extension.extension?.toString());
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// Fire cleanup completion event
|
|
379
|
+
const event = {
|
|
380
|
+
tenantId,
|
|
381
|
+
cleanedAt: new Date().toISOString(),
|
|
382
|
+
extensionsRemoved: result.removed,
|
|
383
|
+
extensionsDeprecated: result.deprecated,
|
|
384
|
+
warnings: result.warnings,
|
|
385
|
+
dryRun: dryRun || false
|
|
386
|
+
};
|
|
387
|
+
// Fire cleanup completed event
|
|
388
|
+
this.onExtensionCleanupCompleted.fire(event);
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Check if update is a security update (simplified logic)
|
|
393
|
+
*/
|
|
394
|
+
isSecurityUpdate(_currentVersion, _targetVersion) {
|
|
395
|
+
// Simple heuristic - in a real implementation, this would check
|
|
396
|
+
// security advisories or version metadata
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
InstalledExtensionService = InstalledExtensionService_1 = __decorate([injectable(), __param(0, inject(SERVER_TYPES.IInstalledExtensionRepository)), __param(1, inject(SERVER_TYPES.IRegistryExtensionService)), __param(2, inject(CommonType.MOLECULER_BROKER)), __param(3, inject(CommonType.LOGGER)), __metadata("design:paramtypes", [Object, Object, ServiceBroker, Object])], InstalledExtensionService);export{InstalledExtensionService};//# sourceMappingURL=installed-extension-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installed-extension-service.js","sources":["../../src/services/installed-extension-service.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installed-extension-service.test.d.ts","sourceRoot":"","sources":["../../src/services/installed-extension-service.test.ts"],"names":[],"mappings":""}
|
package/lib/store/index.d.ts
CHANGED
package/lib/store/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/store/models/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/store/models/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { IInstalledExtensionModel } from 'common/server';
|
|
2
|
+
import { Connection, Schema } from 'mongoose';
|
|
3
|
+
export declare const InstalledExtensionModelFunc: (db: Connection) => import("mongoose").Model<IInstalledExtensionModel, {}, {}, {}, Schema<IInstalledExtensionModel, import("mongoose").Model<IInstalledExtensionModel, any, any, any, any>, {}, {}, {}, {}, import("mongoose").DefaultSchemaOptions, IInstalledExtensionModel>>;
|
|
4
|
+
//# sourceMappingURL=installed-extension-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installed-extension-model.d.ts","sourceRoot":"","sources":["../../../src/store/models/installed-extension-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,wBAAwB,EAQ3B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAyR9C,eAAO,MAAM,2BAA2B,GAAI,IAAI,UAAU,gQACe,CAAC"}
|