Skip to content

Commit

Permalink
clean up jsAPI, improve caching
Browse files Browse the repository at this point in the history
  • Loading branch information
iJungleboy committed Nov 27, 2024
1 parent 6ee3d3c commit 71ebab5
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 50 deletions.
10 changes: 5 additions & 5 deletions Src/Dnn/ToSic.Sxc.Dnn.Core/Dnn/Web/DnnJsApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ public JsApi GetJsApi(int? pageId, string siteRoot, string rvt, bool withPublicK
// pageId and siteRoot are normally null when called from razor, api, custom cs
// pageId and siteRoot are provided only in very special case for EditUI in /DesktopModules/.../...aspx

string SiteRootFn() => siteRoot ?? ServicesFramework.GetServiceFrameworkRoot();
string SecureEndpointPrimaryKeyFn() => withPublicKey ? rsaCryptographyService.PublicKey : null;

return jsApiCache.JsApiJson(
platform: PlatformType.Dnn.ToString(),
pageId: pageId ?? PortalSettings.Current.ActiveTab.TabID,
Expand All @@ -34,10 +31,13 @@ public JsApi GetJsApi(int? pageId, string siteRoot, string rvt, bool withPublicK
appApiRoot: () => GetApiRoots(SiteRootFn()).AppApiRoot,
uiRoot: () => VirtualPathUtility.ToAbsolute(DnnConstants.SysFolderRootVirtual),
rvtHeader: DnnConstants.AntiForgeryTokenHeaderName,
rvt: AntiForgeryToken,
secureEndpointPublicKey: SecureEndpointPrimaryKeyFn,
rvt: AntiForgeryToken,
withPublicKey: withPublicKey,
secureEndpointPublicKey: () => rsaCryptographyService.PublicKey,
dialogQuery: $"{PortalIdParamName}={PortalSettings.Current.PortalId}"
);

string SiteRootFn() => siteRoot ?? ServicesFramework.GetServiceFrameworkRoot();
}

internal static (string SiteApiRoot, string AppApiRoot) GetApiRoots(string siteRoot = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ internal class OqtJsApiService(
: ServiceBase("OqtJsApi", connect: [antiForgery, http, jsApiCache, aliasResolver, rsaCryptographyService]), IJsApiService
{
public string GetJsApiJson(int? pageId = null, string siteRoot = null, string rvt = null, bool withPublicKey = false)
=> JsApi.JsApiJson(GetJsApi(pageId, siteRoot, rvt));
=> JsApi.JsApiJson(GetJsApi(pageId, siteRoot, rvt, withPublicKey));

public JsApi GetJsApi(int? pageId = null, string siteRoot = null, string rvt = null, bool withPublicKey = false)
{
Expand All @@ -33,14 +33,15 @@ public JsApi GetJsApi(int? pageId = null, string siteRoot = null, string rvt = n
uiRoot: UiRootFn,
rvtHeader: Oqtane.Shared.Constants.AntiForgeryTokenHeaderName,
rvt: RvtFn,
withPublicKey: withPublicKey,
secureEndpointPublicKey: SecureEndpointPrimaryKeyFn,
dialogQuery: null);

string SiteRootFn() => siteRoot.IsEmpty() ? OqtPageOutput.GetSiteRoot(aliasResolver.Alias) : siteRoot;
string ApiRootFn() => SiteRootFn() + OqtWebApiConstants.ApiRootNoLanguage + "/";
string UiRootFn() => OqtConstants.UiRoot + "/";
string RvtFn() => rvt.IsEmpty() && http?.HttpContext != null ? antiForgery.GetAndStoreTokens(http.HttpContext).RequestToken : rvt;
string SecureEndpointPrimaryKeyFn() => withPublicKey ? rsaCryptographyService.PublicKey : null;
string SecureEndpointPrimaryKeyFn() => rsaCryptographyService.PublicKey;
}

}
31 changes: 13 additions & 18 deletions Src/Sxc/ToSic.Sxc/Web/Internal/JsContext/JsApi.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// ReSharper disable InconsistentNaming
#pragma warning disable IDE1006
using System.Text.Json;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ToSic.Sxc.Web.Internal.JsContext;
Expand All @@ -16,44 +14,41 @@ public class JsApi
public const string ExtensionPlaceholder = "e.x.t";

[JsonPropertyName("platform")]
public string platform { get; set; }
public string Platform { get; set; }

[JsonPropertyName("page")]
public int page { get; set; }
public int Page { get; set; }

[JsonPropertyName("root")]
public string root { get; set; }
public string Root { get; set; }

[JsonPropertyName("api")]
public string api { get; set; }
public string Api { get; set; }

[JsonPropertyName("appApi")]
public string appApi { get; set; }
public string AppApi { get; set; }

[JsonPropertyName("uiRoot")]
public string uiRoot { get; set; }
public string UiRoot { get; set; }

[JsonPropertyName("rvtHeader")]
public string rvtHeader { get; set; }
public string RvtHeader { get; set; }

[JsonPropertyName("rvt")]
public string rvt { get; set; }
public string Rvt { get; set; }

[JsonPropertyName("dialogQuery")]
public string dialogQuery { get; set; }
public string DialogQuery { get; set; }

// TODO: should probably get a nicer name, like "publicKey" or "publicKeyForEncryption"
// ...but change would require JS changes as well

[JsonPropertyName("secureEndpointPublicKey")]
[JsonPropertyName("publicKey")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string secureEndpointPublicKey { get; set; }
public string PublicKey { get; set; }

/// <summary>
/// Debug information while we're developing the on-module info
/// </summary>
[JsonIgnore]
public string source => "module JsApi";
public string Source => "module JsApi";

public static string JsApiJson(JsApi jsApi) => JsonSerializer.Serialize(jsApi);

Expand Down
56 changes: 31 additions & 25 deletions Src/Sxc/ToSic.Sxc/Web/Internal/JsContext/JsApiCacheService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,60 @@ namespace ToSic.Sxc.Web.Internal.JsContext;
public class JsApiCacheService(IHttp http) : ServiceBase("JsApi", connect: [http])
{
private const string JsApiKey = "JsApi";
private ConcurrentDictionary<int, JsApi> _cache;

/// <summary>
/// Create the JsAPI - or get from cache.
/// This is why we provide functions for most properties, so they don't need to be accessed if not needed.
/// </summary>
/// <returns></returns>
public JsApi JsApiJson(string platform,
public JsApi JsApiJson(
string platform,
int pageId,
Func<string> siteRoot,
Func<string> apiRoot,
Func<string> appApiRoot,
Func<string> uiRoot,
string rvtHeader,
Func<string> rvt,
bool withPublicKey,
Func<string> secureEndpointPublicKey,
string dialogQuery = null // these are any platform specific url query params to the dialog; can be null
)
{
if (_cache == null)
{
if (http.Current.Items[JsApiKey] != null)
_cache = (ConcurrentDictionary<int, JsApi>)http.Current.Items[JsApiKey];
if (_cache == null)
{
_cache = new();
http.Current.Items[JsApiKey] = _cache;
}
}

if (_cache.TryGetValue(pageId, out var jsApi))
// Minor hack: if with secure endpoint, use negative page-id for cache
var cacheKey = withPublicKey ? -pageId : pageId;

if (Cache.TryGetValue(cacheKey, out var jsApi))
return jsApi;

jsApi = new()
{
platform = platform.ToLowerInvariant(),
page = pageId,
root = siteRoot.Invoke(),
api = apiRoot.Invoke(),
appApi = appApiRoot.Invoke(),
uiRoot = uiRoot.Invoke(),
rvtHeader = rvtHeader,
rvt = rvt.Invoke(),
dialogQuery = dialogQuery,
secureEndpointPublicKey = secureEndpointPublicKey.Invoke()
Platform = platform.ToLowerInvariant(),
Page = pageId,
Root = siteRoot.Invoke(),
Api = apiRoot.Invoke(),
AppApi = appApiRoot.Invoke(),
UiRoot = uiRoot.Invoke(),
RvtHeader = rvtHeader,
Rvt = rvt.Invoke(),
DialogQuery = dialogQuery,
PublicKey = withPublicKey ? secureEndpointPublicKey.Invoke() : null,
};
_cache.AddOrUpdate(pageId, jsApi, (key, value) => jsApi);
Cache.AddOrUpdate(cacheKey, jsApi, (key, value) => jsApi);

return jsApi;
}

private ConcurrentDictionary<int, JsApi> Cache => field ??= GetCache();

private ConcurrentDictionary<int, JsApi> GetCache()
{
if (http.Current.Items[JsApiKey] is ConcurrentDictionary<int, JsApi> cache)
return cache;
cache = new();
http.Current.Items[JsApiKey] = cache;
return cache;
}


}

0 comments on commit 71ebab5

Please sign in to comment.