11
11
using Microsoft . AspNetCore . Http . Features ;
12
12
using Microsoft . AspNetCore . Mvc ;
13
13
using Microsoft . AspNetCore . WebUtilities ;
14
+ using Microsoft . Extensions . FileProviders ;
14
15
using Microsoft . Extensions . Options ;
15
16
using Microsoft . Net . Http . Headers ;
16
17
using VirtoCommerce . Platform . Core ;
@@ -42,6 +43,7 @@ public class ModulesController : Controller
42
43
private readonly IPlatformRestarter _platformRestarter ;
43
44
private static readonly object _lockObject = new object ( ) ;
44
45
private static readonly FormOptions _defaultFormOptions = new FormOptions ( ) ;
46
+ private readonly ILocalModuleCatalog _localModuleCatalog ;
45
47
46
48
public ModulesController (
47
49
IExternalModuleCatalog externalModuleCatalog ,
@@ -52,7 +54,8 @@ public ModulesController(
52
54
IOptions < PlatformOptions > platformOptions ,
53
55
IOptions < ExternalModuleCatalogOptions > externalModuleCatalogOptions ,
54
56
IOptions < LocalStorageModuleCatalogOptions > localStorageModuleCatalogOptions ,
55
- IPlatformRestarter platformRestarter )
57
+ IPlatformRestarter platformRestarter ,
58
+ ILocalModuleCatalog localModuleCatalog )
56
59
{
57
60
_externalModuleCatalog = externalModuleCatalog ;
58
61
_moduleInstaller = moduleInstaller ;
@@ -63,6 +66,7 @@ public ModulesController(
63
66
_externalModuleCatalogOptions = externalModuleCatalogOptions . Value ;
64
67
_localStorageModuleCatalogOptions = localStorageModuleCatalogOptions . Value ;
65
68
_platformRestarter = platformRestarter ;
69
+ _localModuleCatalog = localModuleCatalog ;
66
70
}
67
71
68
72
/// <summary>
@@ -90,11 +94,48 @@ public ActionResult<ModuleDescriptor[]> GetModules()
90
94
{
91
95
EnsureModulesCatalogInitialized ( ) ;
92
96
93
- var retVal = _externalModuleCatalog . Modules . OfType < ManifestModuleInfo > ( ) . OrderBy ( x => x . Id ) . ThenBy ( x => x . Version )
94
- . Select ( x => new ModuleDescriptor ( x ) )
95
- . ToArray ( ) ;
97
+ var allModules = _externalModuleCatalog . Modules
98
+ . OfType < ManifestModuleInfo > ( )
99
+ . OrderBy ( x => x . Id )
100
+ . ThenBy ( x => x . Version )
101
+ . Select ( x => new ModuleDescriptor ( x ) )
102
+ . ToList ( ) ;
96
103
97
- return Ok ( retVal ) ;
104
+ _localModuleCatalog . Initialize ( ) ;
105
+ var localModules = _localModuleCatalog . Modules . OfType < ManifestModuleInfo > ( ) . ToDictionary ( x => x . Id ) ;
106
+
107
+ foreach ( var module in allModules . Where ( x => ! string . IsNullOrEmpty ( x . IconUrl ) ) )
108
+ {
109
+ module . IconUrl = localModules . TryGetValue ( module . Id , out var localModule ) && IconFileExists ( localModule )
110
+ ? localModule . IconUrl
111
+ : null ;
112
+ }
113
+
114
+ return Ok ( allModules ) ;
115
+ }
116
+
117
+ private static bool IconFileExists ( ManifestModuleInfo module )
118
+ {
119
+ // PathString should start from "/"
120
+ var moduleIconUrl = module . IconUrl ;
121
+ if ( ! moduleIconUrl . StartsWith ( '/' ) )
122
+ {
123
+ moduleIconUrl = "/" + moduleIconUrl ;
124
+ }
125
+
126
+ var basePath = new PathString ( $ "/modules/$({ module . Id } )") ;
127
+ var iconUrlPath = new PathString ( moduleIconUrl ) ;
128
+
129
+ if ( ! iconUrlPath . StartsWithSegments ( basePath , out var subPath ) ||
130
+ string . IsNullOrEmpty ( subPath . Value ) ||
131
+ ! Directory . Exists ( module . FullPhysicalPath ) )
132
+ {
133
+ return false ;
134
+ }
135
+
136
+ using var fileProvider = new PhysicalFileProvider ( module . FullPhysicalPath ) ;
137
+
138
+ return fileProvider . GetFileInfo ( subPath . Value ) . Exists ;
98
139
}
99
140
100
141
/// <summary>
@@ -167,66 +208,97 @@ public async Task<ActionResult<ModuleDescriptor>> UploadModuleArchive()
167
208
return BadRequest ( $ "Expected a multipart request, but got { Request . ContentType } ") ;
168
209
}
169
210
170
- var uploadPath = Path . GetFullPath ( _platformOptions . LocalUploadFolderPath ) ;
171
- if ( ! Directory . Exists ( uploadPath ) )
211
+ var targetFilePath = await UploadFile ( Request , Path . GetFullPath ( _platformOptions . LocalUploadFolderPath ) ) ;
212
+ if ( targetFilePath is null )
213
+ {
214
+ return BadRequest ( "Cannot read file" ) ;
215
+ }
216
+
217
+ var manifest = await LoadModuleManifestFromZipArchive ( targetFilePath ) ;
218
+ if ( manifest is null )
172
219
{
173
- Directory . CreateDirectory ( uploadPath ) ;
220
+ return BadRequest ( "Cannot read module manifest" ) ;
174
221
}
175
222
176
- ModuleDescriptor result = null ;
177
- string targetFilePath = null ;
178
- var boundary = MultipartRequestHelper . GetBoundary ( MediaTypeHeaderValue . Parse ( Request . ContentType ) , _defaultFormOptions . MultipartBoundaryLengthLimit ) ;
179
- var reader = new MultipartReader ( boundary , HttpContext . Request . Body ) ;
223
+ var module = AbstractTypeFactory < ManifestModuleInfo > . TryCreateInstance ( ) ;
224
+ module . LoadFromManifest ( manifest ) ;
225
+ var existingModule = _externalModuleCatalog . Modules . OfType < ManifestModuleInfo > ( ) . FirstOrDefault ( x => x . Equals ( module ) ) ;
226
+
227
+ if ( existingModule != null )
228
+ {
229
+ module = existingModule ;
230
+ }
231
+ else
232
+ {
233
+ //Force dependency validation for new module
234
+ _externalModuleCatalog . CompleteListWithDependencies ( [ module ] ) . ToList ( ) . Clear ( ) ;
235
+ _externalModuleCatalog . AddModule ( module ) ;
236
+ }
237
+
238
+ module . Ref = targetFilePath ;
239
+ var result = new ModuleDescriptor ( module ) ;
240
+
241
+ return Ok ( result ) ;
242
+ }
243
+
244
+ private static async Task < string > UploadFile ( HttpRequest request , string uploadFolderPath )
245
+ {
246
+ var boundary = MultipartRequestHelper . GetBoundary ( MediaTypeHeaderValue . Parse ( request . ContentType ) , _defaultFormOptions . MultipartBoundaryLengthLimit ) ;
247
+ var reader = new MultipartReader ( boundary , request . Body ) ;
180
248
var section = await reader . ReadNextSectionAsync ( ) ;
181
249
182
- if ( section ! = null )
250
+ if ( section = = null )
183
251
{
184
- var hasContentDispositionHeader = ContentDispositionHeaderValue . TryParse ( section . ContentDisposition , out var contentDisposition ) ;
252
+ return null ;
253
+ }
185
254
186
- if ( hasContentDispositionHeader )
187
- {
188
- if ( MultipartRequestHelper . HasFileContentDisposition ( contentDisposition ) )
189
- {
190
- var fileName = contentDisposition . FileName . Value ;
191
- targetFilePath = Path . Combine ( uploadPath , fileName ) ;
255
+ if ( ! ContentDispositionHeaderValue . TryParse ( section . ContentDisposition , out var contentDisposition ) ||
256
+ ! MultipartRequestHelper . HasFileContentDisposition ( contentDisposition ) )
257
+ {
258
+ return null ;
259
+ }
192
260
193
- using ( var targetStream = System . IO . File . Create ( targetFilePath ) )
194
- {
195
- await section . Body . CopyToAsync ( targetStream ) ;
196
- }
261
+ var fileName = Path . GetFileName ( contentDisposition . FileName . Value ) ;
262
+ if ( string . IsNullOrEmpty ( fileName ) )
263
+ {
264
+ return null ;
265
+ }
197
266
198
- }
199
- }
200
- using ( var packageStream = System . IO . File . Open ( targetFilePath , FileMode . Open ) )
201
- using ( var package = new ZipArchive ( packageStream , ZipArchiveMode . Read ) )
267
+ if ( ! Directory . Exists ( uploadFolderPath ) )
268
+ {
269
+ Directory . CreateDirectory ( uploadFolderPath ) ;
270
+ }
271
+
272
+ var targetFilePath = Path . Combine ( uploadFolderPath , fileName ) ;
273
+
274
+ await using var targetStream = System . IO . File . Create ( targetFilePath ) ;
275
+ await section . Body . CopyToAsync ( targetStream ) ;
276
+
277
+ return targetFilePath ;
278
+ }
279
+
280
+ private static async Task < ModuleManifest > LoadModuleManifestFromZipArchive ( string path )
281
+ {
282
+ ModuleManifest manifest = null ;
283
+
284
+ try
285
+ {
286
+ await using var packageStream = System . IO . File . Open ( path , FileMode . Open ) ;
287
+ using var package = new ZipArchive ( packageStream , ZipArchiveMode . Read ) ;
288
+
289
+ var entry = package . GetEntry ( "module.manifest" ) ;
290
+ if ( entry != null )
202
291
{
203
- var entry = package . GetEntry ( "module.manifest" ) ;
204
- if ( entry != null )
205
- {
206
- using ( var manifestStream = entry . Open ( ) )
207
- {
208
- var manifest = ManifestReader . Read ( manifestStream ) ;
209
- var module = AbstractTypeFactory < ManifestModuleInfo > . TryCreateInstance ( ) ;
210
- module . LoadFromManifest ( manifest ) ;
211
- var alreadyExistModule = _externalModuleCatalog . Modules . OfType < ManifestModuleInfo > ( ) . FirstOrDefault ( x => x . Equals ( module ) ) ;
212
- if ( alreadyExistModule != null )
213
- {
214
- module = alreadyExistModule ;
215
- }
216
- else
217
- {
218
- //Force dependency validation for new module
219
- _externalModuleCatalog . CompleteListWithDependencies ( new [ ] { module } ) . ToList ( ) . Clear ( ) ;
220
- _externalModuleCatalog . AddModule ( module ) ;
221
- }
222
- module . Ref = targetFilePath ;
223
- result = new ModuleDescriptor ( module ) ;
224
- }
225
- }
292
+ await using var manifestStream = entry . Open ( ) ;
293
+ manifest = ManifestReader . Read ( manifestStream ) ;
226
294
}
227
295
}
296
+ catch
297
+ {
298
+ // Suppress any exceptions
299
+ }
228
300
229
- return Ok ( result ) ;
301
+ return manifest ;
230
302
}
231
303
232
304
/// <summary>
0 commit comments