// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using System.Security.Claims; using Jint; using Jint.Native; using Jint.Runtime; using Jint.Runtime.Interop; using Microsoft.Extensions.DependencyInjection; using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Infrastructure; using Squidex.Infrastructure.Tasks; namespace Squidex.Domain.Apps.Entities.Assets { public sealed class AssetsJintExtension : IJintExtension { private delegate void GetAssetsDelegate(JsValue references, Action callback); private delegate void GetAssetTextDelegate(JsValue references, Action callback, JsValue encoding); private readonly IServiceProvider serviceProvider; public AssetsJintExtension(IServiceProvider serviceProvider) { this.serviceProvider = serviceProvider; } public void ExtendAsync(ScriptExecutionContext context) { AddAssetText(context); AddAsset(context); } private void AddAsset(ScriptExecutionContext context) { if (!context.TryGetValue("appId", out var appId)) { return; } if (!context.TryGetValue("user", out var user)) { return; } var action = new GetAssetsDelegate((references, callback) => GetAssets(context, appId, user, references, callback)); context.Engine.SetValue("getAsset", action); context.Engine.SetValue("getAssets", action); } private void AddAssetText(ScriptExecutionContext context) { var action = new GetAssetTextDelegate((references, callback, encoding) => GetText(context, references, callback, encoding)); context.Engine.SetValue("getAssetText", action); } private void GetText(ScriptExecutionContext context, JsValue input, Action callback, JsValue encoding) { GetTextAsync(context, input, callback, encoding).Forget(); } private async Task GetTextAsync(ScriptExecutionContext context, JsValue input, Action callback, JsValue encoding) { Guard.NotNull(callback, nameof(callback)); if (input is not ObjectWrapper objectWrapper) { callback(JsValue.FromObject(context.Engine, "ErrorNoAsset")); return; } async Task ResolveAssetText(DomainId appId, DomainId id, long fileSize, long fileVersion) { if (fileSize > 256_000) { callback(JsValue.FromObject(context.Engine, "ErrorTooBig")); return; } context.MarkAsync(); try { var assetFileStore = serviceProvider.GetRequiredService(); var encoded = await assetFileStore.GetTextAsync(appId, id, fileVersion, encoding?.ToString()); callback(JsValue.FromObject(context.Engine, encoded)); } catch (Exception ex) { context.Fail(ex); } } switch (objectWrapper.Target) { case IAssetEntity asset: await ResolveAssetText(asset.AppId.Id, asset.Id, asset.FileSize, asset.FileVersion); return; case EnrichedAssetEvent @event: await ResolveAssetText(@event.AppId.Id, @event.Id, @event.FileSize, @event.FileVersion); return; } callback(JsValue.FromObject(context.Engine, "ErrorNoAsset")); } private void GetAssets(ScriptExecutionContext context, DomainId appId, ClaimsPrincipal user, JsValue references, Action callback) { GetReferencesAsync(context, appId, user, references, callback).Forget(); } private async Task GetReferencesAsync(ScriptExecutionContext context, DomainId appId, ClaimsPrincipal user, JsValue references, Action callback) { Guard.NotNull(callback, nameof(callback)); var ids = new List(); if (references.IsString()) { ids.Add(DomainId.Create(references.ToString())); } else if (references.IsArray()) { foreach (var value in references.AsArray()) { if (value.IsString()) { ids.Add(DomainId.Create(value.ToString())); } } } if (ids.Count == 0) { var emptyAssets = Array.Empty(); callback(JsValue.FromObject(context.Engine, emptyAssets)); return; } context.MarkAsync(); try { var app = await GetAppAsync(appId); var requestContext = new Context(user, app).Clone(b => b .WithoutTotal()); var assetQuery = serviceProvider.GetRequiredService(); var assetItems = await assetQuery.QueryAsync(requestContext, null, Q.Empty.WithIds(ids), context.CancellationToken); callback(JsValue.FromObject(context.Engine, assetItems.ToArray())); } catch (Exception ex) { context.Fail(ex); } } private async Task GetAppAsync(DomainId appId) { var appProvider = serviceProvider.GetRequiredService(); var app = await appProvider.GetAppAsync(appId); if (app == null) { throw new JavaScriptException("App does not exist."); } return app; } } }