aboutsummaryrefslogtreecommitdiff
path: root/BackEnd/Timeline
diff options
context:
space:
mode:
Diffstat (limited to 'BackEnd/Timeline')
-rw-r--r--BackEnd/Timeline/Controllers/TimelinePostController.cs16
-rw-r--r--BackEnd/Timeline/SignalRHub/ITimelineClient.cs9
-rw-r--r--BackEnd/Timeline/SignalRHub/TimelineHub.cs58
-rw-r--r--BackEnd/Timeline/Startup.cs4
-rw-r--r--BackEnd/Timeline/Timeline.csproj140
5 files changed, 156 insertions, 71 deletions
diff --git a/BackEnd/Timeline/Controllers/TimelinePostController.cs b/BackEnd/Timeline/Controllers/TimelinePostController.cs
index da45cbea..f00a689c 100644
--- a/BackEnd/Timeline/Controllers/TimelinePostController.cs
+++ b/BackEnd/Timeline/Controllers/TimelinePostController.cs
@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.SignalR;
+using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -13,6 +15,7 @@ using Timeline.Models.Validation;
using Timeline.Services.Mapper;
using Timeline.Services.Timeline;
using Timeline.Services.User;
+using Timeline.SignalRHub;
namespace Timeline.Controllers
{
@@ -24,6 +27,8 @@ namespace Timeline.Controllers
[ProducesErrorResponseType(typeof(CommonResponse))]
public class TimelinePostController : MyControllerBase
{
+ private readonly ILogger<TimelinePostController> _logger;
+
private readonly ITimelineService _timelineService;
private readonly ITimelinePostService _postService;
@@ -31,12 +36,16 @@ namespace Timeline.Controllers
private readonly MarkdownProcessor _markdownProcessor;
- public TimelinePostController(ITimelineService timelineService, ITimelinePostService timelinePostService, IGenericMapper mapper, MarkdownProcessor markdownProcessor)
+ private readonly IHubContext<TimelineHub> _timelineHubContext;
+
+ public TimelinePostController(ILogger<TimelinePostController> logger, ITimelineService timelineService, ITimelinePostService timelinePostService, IGenericMapper mapper, MarkdownProcessor markdownProcessor, IHubContext<TimelineHub> timelineHubContext)
{
+ _logger = logger;
_timelineService = timelineService;
_postService = timelinePostService;
_mapper = mapper;
_markdownProcessor = markdownProcessor;
+ _timelineHubContext = timelineHubContext;
}
private bool UserHasAllTimelineManagementPermission => UserHasPermission(UserPermission.AllTimelineManagement);
@@ -207,6 +216,11 @@ namespace Timeline.Controllers
try
{
var post = await _postService.CreatePostAsync(timelineId, userId, createRequest);
+
+ var group = TimelineHub.GenerateTimelinePostChangeListeningGroupName(timeline);
+ await _timelineHubContext.Clients.Group(group).SendAsync(nameof(ITimelineClient.OnTimelinePostChanged), timeline);
+ _logger.LogInformation("Notify group {0} of timeline post change.", group);
+
var result = await Map(post);
return result;
}
diff --git a/BackEnd/Timeline/SignalRHub/ITimelineClient.cs b/BackEnd/Timeline/SignalRHub/ITimelineClient.cs
new file mode 100644
index 00000000..0d1be093
--- /dev/null
+++ b/BackEnd/Timeline/SignalRHub/ITimelineClient.cs
@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+
+namespace Timeline.SignalRHub
+{
+ public interface ITimelineClient
+ {
+ Task OnTimelinePostChanged(string timelineName);
+ }
+}
diff --git a/BackEnd/Timeline/SignalRHub/TimelineHub.cs b/BackEnd/Timeline/SignalRHub/TimelineHub.cs
new file mode 100644
index 00000000..2ad7bd66
--- /dev/null
+++ b/BackEnd/Timeline/SignalRHub/TimelineHub.cs
@@ -0,0 +1,58 @@
+using Microsoft.AspNetCore.SignalR;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Threading.Tasks;
+using Timeline.Auth;
+using Timeline.Services;
+using Timeline.Services.Timeline;
+using Timeline.Services.User;
+
+namespace Timeline.SignalRHub
+{
+ public class TimelineHub : Hub<ITimelineClient>
+ {
+ private readonly ILogger<TimelineHub> _logger;
+ private readonly ITimelineService _timelineService;
+
+ public TimelineHub(ILogger<TimelineHub> logger, ITimelineService timelineService)
+ {
+ _logger = logger;
+ _timelineService = timelineService;
+ }
+
+ public static string GenerateTimelinePostChangeListeningGroupName(string timelineName)
+ {
+ return $"timeline-post-change-{timelineName}";
+ }
+
+ public async Task SubscribeTimelinePostChange(string timelineName)
+ {
+ try
+ {
+ var timelineId = await _timelineService.GetTimelineIdByNameAsync(timelineName);
+ var user = Context.User;
+ if (!user.HasPermission(UserPermission.AllTimelineManagement) && !await _timelineService.HasReadPermissionAsync(timelineId, user.GetOptionalUserId()))
+ throw new HubException("Forbidden.");
+
+ var group = GenerateTimelinePostChangeListeningGroupName(timelineName);
+ await Groups.AddToGroupAsync(Context.ConnectionId, group);
+ _logger.LogInformation("Add connection {0} to group {1}", Context.ConnectionId, group);
+ }
+ catch (ArgumentException)
+ {
+ throw new HubException("Timeline name is illegal.");
+ }
+ catch (EntityNotExistException)
+ {
+ throw new HubException("Timeline not exist.");
+ }
+ }
+
+ public async Task UnsubscribeTimelinePostChange(string timelineName)
+ {
+ var group = GenerateTimelinePostChangeListeningGroupName(timelineName);
+ await Groups.RemoveFromGroupAsync(Context.ConnectionId, group);
+ _logger.LogInformation("Remove connection {0} from group {1}", Context.ConnectionId, group);
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Startup.cs b/BackEnd/Timeline/Startup.cs
index 1e0d4779..18097e2c 100644
--- a/BackEnd/Timeline/Startup.cs
+++ b/BackEnd/Timeline/Startup.cs
@@ -26,6 +26,7 @@ using Timeline.Services.Mapper;
using Timeline.Services.Timeline;
using Timeline.Services.Token;
using Timeline.Services.User;
+using Timeline.SignalRHub;
using Timeline.Swagger;
namespace Timeline
@@ -85,6 +86,8 @@ namespace Timeline
options.InvalidModelStateResponseFactory = InvalidModelResponseFactory.Factory;
});
+ services.AddSignalR();
+
services.AddAuthentication(AuthenticationConstants.Scheme)
.AddScheme<MyAuthenticationOptions, MyAuthenticationHandler>(AuthenticationConstants.Scheme, AuthenticationConstants.DisplayName, o => { });
services.AddAuthorization();
@@ -154,6 +157,7 @@ namespace Timeline
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
+ endpoints.MapHub<TimelineHub>("api/hub/timeline");
});
UnknownEndpointMiddleware.Attach(app);
diff --git a/BackEnd/Timeline/Timeline.csproj b/BackEnd/Timeline/Timeline.csproj
index b9653b25..955947af 100644
--- a/BackEnd/Timeline/Timeline.csproj
+++ b/BackEnd/Timeline/Timeline.csproj
@@ -49,19 +49,19 @@
<ItemGroup>
<Compile Update="Auth\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Controllers\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Filters\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Models\Http\Resource.Designer.cs">
<DesignTime>True</DesignTime>
@@ -69,122 +69,122 @@
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Models\Validation\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Routes\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Services\DatabaseManagement\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Services\Data\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Services\Imaging\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Services\Mapper\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Services\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Services\Timeline\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Services\Token\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Services\User\Avatar\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
<Compile Update="Services\User\Resource.Designer.cs">
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- <DependentUpon>Resource.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ <DependentUpon>Resource.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Auth\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Controllers\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Filters\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Models\Http\Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Models\Validation\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Routes\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Services\DatabaseManagement\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Services\Data\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Services\Imaging\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Services\Mapper\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Services\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Services\Timeline\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Services\Token\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Services\User\Avatar\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Services\User\Resource.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Resource.Designer.cs</LastGenOutput>
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project> \ No newline at end of file