Skip to content

Commit

Permalink
Merge pull request #5 from bielu/feature/functional-dashboard-and-con…
Browse files Browse the repository at this point in the history
…tent-app

Feature/functional dashboard and content app
  • Loading branch information
bielu authored Jan 20, 2024
2 parents 1ea4624 + 1637574 commit 226a2c0
Show file tree
Hide file tree
Showing 23 changed files with 872 additions and 111 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
❤️ This project is royalty free and open source, so if you using and love it, you can support it by becoming [Github sponsor](https://github.com/sponsors/bielu) ❤️


# Umbraco Cdn Integration provider
This package integrate umbraco publishing pipeline to cdn to refresh content based on publish / save operations.
## Docs
Expand Down
1 change: 1 addition & 0 deletions src/bielu.Umbraco.Cdn.Abstraction/Models/AuditRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ namespace bielu.Umbraco.Cdn.Models;

public class AuditRecord
{
public bool IsFromProvider { get; set; }
public string? Name { get; set; }
public DateTime Date { get; set; }
public string? Message { get; set; }
Expand Down
3 changes: 2 additions & 1 deletion src/bielu.Umbraco.Cdn.Core/Composition/CdnComposition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using bielu.Umbraco.Cdn.Core.NotitificationHandlers.Tree;
using bielu.Umbraco.Cdn.Core.Services;
using bielu.Umbraco.Cdn.Services;
using bielu.Umbraco.Cdn.Core.ContentApp;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
Expand All @@ -22,7 +23,7 @@ public void Compose(IUmbracoBuilder composition)
{
composition.Services.AddOptions<BieluCdnOptions>().BindConfiguration(BieluCdnOptions.SectionName);
composition.ManifestFilters().Append<BieluCdnUIManifestFilter>();

composition.ContentApps().Append<BieluCdnApp>();
//services
composition.Services.AddTransient(typeof(IUmbracoUrlDeliveryService), typeof(UmbracoUrlDeliveryService));
composition.Services.AddSingleton<ICdnManager, CdnManager>();
Expand Down
1 change: 1 addition & 0 deletions src/bielu.Umbraco.Cdn.Core/Constants/CdnConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
public class CdnConstants
{
public const string CdnConfigSectionName = "Bielu:Cdn";
public const string AuditLogType = "CDN Refresh";
}
34 changes: 34 additions & 0 deletions src/bielu.Umbraco.Cdn.Core/ContentApp/BieluCDNApp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Models.Membership;

namespace bielu.Umbraco.Cdn.Core.ContentApp;

public class BieluCdnApp : IContentAppFactory
{
public global::Umbraco.Cms.Core.Models.ContentEditing.ContentApp? GetContentAppFor(object source, IEnumerable<IReadOnlyUserGroup> userGroups)
{
// Can implement some logic with userGroups if needed
// Allowing us to display the content app with some restrictions for certain groups
if (userGroups.All(x => x.Alias.ToLowerInvariant() != global::Umbraco.Cms.Core.Constants.Security.AdminGroupAlias))
return null;

// Only show app on content items
if (!(source is IContent))
return null;
// Only show app on content with certain content type alias
// if (!content.ContentType.Alias.Equals("aliasName"))
// return null;



return new global::Umbraco.Cms.Core.Models.ContentEditing.ContentApp
{
Alias = "bieluCdnApp",
Name = "CDN",
Icon = "icon-hard-drive",
View = "/App_Plugins/bielu.cdn.ui/contentApp.html",
Weight = 900
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,56 +28,110 @@ public class BieluCdnManagmentController : UmbracoApiController
private BieluCdnOptions _optionsMonitor;

public BieluCdnManagmentController(ICdnManager manager, IUmbracoContextFactory contextFactory,
Services.ICdnAuditService auditService, IUmbracoUrlDeliveryService urlDeliveryService, IOptionsMonitor<BieluCdnOptions> optionsMonitor)
Services.ICdnAuditService auditService, IUmbracoUrlDeliveryService urlDeliveryService,
IOptionsMonitor<BieluCdnOptions> optionsMonitor)
{
_manager = manager;
_contextFactory = contextFactory;
_auditService = auditService;
_urlDeliveryService = urlDeliveryService;
_optionsMonitor = optionsMonitor.CurrentValue;
optionsMonitor.OnChange((options, name) =>
{
_optionsMonitor = options;
});
optionsMonitor.OnChange((options, name) => { _optionsMonitor = options; });
}

public async Task<IEnumerable<AuditRecord>> GetAuditHistory()
public async Task<IEnumerable<AuditRecord>?> GetAuditHistory()
{
return await _auditService.GetAllRecords();
}

public async Task<IEnumerable<AuditRecord>?> GetAllRecords(int id)
{
return await _auditService.GetAllRecords(id);
}
public async Task<AuditRecord?> GetLastLog(int id)
{
return await _auditService.GetLastRecord(id);
}
public async Task<IEnumerable<Provider>> GetProviders(int id = -1)
{
return await _manager.GetProviders(id);
}

public async Task<Status> RefreshForNode(int id,bool descandants,bool references, string providerId = null, string domain = null)
public async Task<Status> RefreshDomain(string providerId = null, string domain = null)
{
List<Status> statuses = new List<Status>();
using (var contextReference = _contextFactory.EnsureUmbracoContext())
{
var hostnames = new List<string> { domain };

if (!string.IsNullOrEmpty(providerId))
{
var service = await _manager.GetService(providerId);
statuses.AddRange(await service.PurgeByAssignedHostnames(hostnames));
return statuses.Merge();
}


foreach (var service in (await _manager.GetServices()).Where(x => x.IsEnabled()))
{
statuses.AddRange(await service.PurgeByAssignedHostnames(hostnames));
}
}

return statuses.Merge();
}

public async Task<Status> RefreshForNode(int id, bool descandants, bool references, string providerId = null,
string domain = null)
{
List<Status> statuses = new List<Status>();
using (var contextReference = _contextFactory.EnsureUmbracoContext())
{
var content = contextReference.UmbracoContext.Content.GetById(id);

var urls = _urlDeliveryService.GetUrlsByContent(content, descandants, references);

if (_optionsMonitor.ReferencePurge && references)
{
urls.AddRange(_urlDeliveryService.GetUrlsByReferences(content));
}
if (_optionsMonitor.Auditing)
{
await _auditService.LogRefresh( content.Id,descandants, references);
}
if (!string.IsNullOrEmpty(providerId))
{
var service = await _manager.GetService(providerId);
statuses.AddRange(await service.PurgePages(urls));
statuses.AddRange(await service.PurgePages(urls.Where(x=>x.Contains(domain))));
return statuses.Merge();
}

foreach (var service in (await _manager.GetServices()).Where(x=>x.IsEnabled()))

foreach (var service in (await _manager.GetServices()).Where(x => x.IsEnabled()))
{
statuses.AddRange(await service.PurgePages(urls));
}
}

return statuses.Merge();
}
public async Task<Status> RefreshForProvider(string providerId)
{
List<Status> statuses = new List<Status>();
var service = await _manager.GetService(providerId);

statuses.AddRange(await service.PurgeAll());

return statuses.Merge();
}
public async Task<Status> RefreshAll()
{
List<Status> statuses = new List<Status>();

foreach (var service in (await _manager.GetServices()).Where(x => x.IsEnabled()))
{
statuses.AddRange(await service.PurgeAll());
}

return statuses.Merge();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public void Filter(List<PackageManifest> manifests)
{
$"/App_Plugins/bielu.cdn.ui/{(_options.DevMode ? "dev-bootstrapper" : "bootstrapper")}.js",
"/App_Plugins/bielu.cdn.ui/bielu.cdn.ui.angularController.js",
"/App_Plugins/bielu.cdn.ui/bielu.cdn.ui.angularController.contentApp.js",

},
Version = assembly.GetName()?.Version?.ToString(3) ?? "0.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,28 @@ public class ContentEventsNotificationNotificationHandler : INotificationAsyncHa
private readonly IUmbracoUrlDeliveryService _umbracoUrlDeliveryService;
private readonly IEnumerable<ICdnService> _cdnServices;
private readonly ILogger<ContentEventsNotificationNotificationHandler> _logger;
private readonly IAuditService _auditService;
private readonly IBackOfficeSecurityAccessor _accessor;
private readonly BieluCdnOptions _configuration;
private readonly ICdnAuditService _auditService;
private BieluCdnOptions _configuration;

public ContentEventsNotificationNotificationHandler(IUmbracoUrlDeliveryService umbracoUrlDeliveryService,
IEnumerable<ICdnService> cdnServices, ILogger<ContentEventsNotificationNotificationHandler> logger,
IAuditService auditService, IBackOfficeSecurityAccessor accessor, IOptionsMonitor<BieluCdnOptions> configuration)
ICdnAuditService auditService, IOptionsMonitor<BieluCdnOptions> configuration)
{
_umbracoUrlDeliveryService = umbracoUrlDeliveryService;
_cdnServices = cdnServices;
_logger = logger;
_auditService = auditService;
_accessor = accessor;
_configuration = configuration.CurrentValue;
configuration.OnChange((options, name) => { _configuration = options; });

}

public async Task HandleAsync(ContentMovingNotification notification, CancellationToken cancellationToken)
{
var pages = new List<string>();
var currentUser = _accessor.BackOfficeSecurity.CurrentUser;
foreach (var content in notification.MoveInfoCollection)
{
pages.AddRange(GetPages(content.Entity));
pages.AddRange(await GetPages(content.Entity));
}

//todo: optimize as now we dont valide which domains is valid for either of cdns
Expand Down Expand Up @@ -86,14 +84,12 @@ public async Task HandleAsync(ContentMovingNotification notification, Cancellati
}
}

private List<string> GetPages(IContent content)
private async Task<List<string>> GetPages(IContent content)
{
var currentUser = _accessor.BackOfficeSecurity.CurrentUser;
var pages = new List<string>();
if (_configuration.Auditing)
{
_auditService.Add(AuditType.Custom, currentUser.Id, content.Id, "CDN Refresh",
$"CDN cache was purged", $"CDN cache purged");
await _auditService.LogRefresh( content.Id);
}

pages.AddRange(_umbracoUrlDeliveryService.GetUrlsByContent(content));
Expand All @@ -110,7 +106,7 @@ public async Task HandleAsync(ContentDeletingNotification notification, Cancella

foreach (var content in notification.DeletedEntities)
{
pages.AddRange(GetPages(content));
pages.AddRange(await GetPages(content));
}

//todo: optimize as now we dont valide which domains is valid for either of cdns
Expand Down Expand Up @@ -157,7 +153,6 @@ public async Task HandleAsync(ContentDeletingNotification notification, Cancella

public async Task HandleAsync(ContentUnpublishedNotification notification, CancellationToken cancellationToken)
{
var currentUser = _accessor.BackOfficeSecurity.CurrentUser;
if (!notification.State.ContainsKey("purgedUrls"))
{
return;
Expand Down Expand Up @@ -208,12 +203,10 @@ public async Task HandleAsync(ContentUnpublishedNotification notification, Cance

public async Task HandleAsync(ContentPublishedNotification notification, CancellationToken cancellationToken)
{
var currentUser = _accessor.BackOfficeSecurity.CurrentUser;

var pages = new List<string>();
foreach (var content in notification.PublishedEntities)
{
pages.AddRange(GetPages(content));
pages.AddRange(await GetPages(content));
}

try
Expand Down Expand Up @@ -261,7 +254,7 @@ public async Task HandleAsync(ContentUnpublishingNotification notification, Canc
var pages = new List<string>();
foreach (var content in notification.UnpublishedEntities)
{
pages.AddRange(GetPages(content));
pages.AddRange(await GetPages(content));
}

notification.State["purgedUrls"] = pages;
Expand All @@ -271,15 +264,14 @@ public async Task HandleAsync(ContentPublishingNotification notification, Cancel
{
try
{
var currentUser = _accessor.BackOfficeSecurity.CurrentUser;
var pages = new List<string>();
var pages = new List<string>();


foreach (var content in notification.PublishedEntities)
{
if (content.Published)
{
pages.AddRange(GetPages(content));
pages.AddRange(await GetPages(content));
}
}

Expand All @@ -299,15 +291,12 @@ public async Task HandleAsync(ContentSavedNotification notification, Cancellatio

try
{
var currentUser = _accessor.BackOfficeSecurity.CurrentUser;
var pages = new List<string>();


foreach (var content in notification.SavedEntities)
foreach (var content in notification.SavedEntities)
{
if (content.Published)
{
pages.AddRange(GetPages(content));
pages.AddRange(await GetPages(content));
}
}

Expand Down
Loading

0 comments on commit 226a2c0

Please sign in to comment.