Skip to content

Commit 19dd243

Browse files
authored
Merge pull request #9 from argon-chat/feature/servers
Server related grains
2 parents 3227f97 + 0635302 commit 19dd243

25 files changed

+532
-48
lines changed

src/Argon.Api/Argon.Api.csproj

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,31 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="ActualLab.Fusion" Version="9.5.52" />
12-
<PackageReference Include="ActualLab.Fusion.EntityFramework.Npgsql" Version="9.5.52" />
13-
<PackageReference Include="ActualLab.Rpc.Server" Version="9.5.52" />
14-
<PackageReference Include="Argon.Sfu.Protocol" Version="1.26.0" />
15-
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
16-
<PackageReference Include="Flurl.Http" Version="4.0.2" />
17-
<PackageReference Include="Flurl.Http.Newtonsoft" Version="0.9.1" />
18-
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
11+
<PackageReference Include="Flurl.Http" Version="4.0.2"/>
12+
<PackageReference Include="Flurl.Http.Newtonsoft" Version="0.9.1"/>
13+
<PackageReference Include="MemoryPack" Version="1.21.3"/>
14+
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10"/>
15+
<PackageReference Include="ActualLab.Fusion" Version="9.5.52"/>
16+
<PackageReference Include="ActualLab.Fusion.EntityFramework.Npgsql" Version="9.5.52"/>
17+
<PackageReference Include="ActualLab.Rpc.Server" Version="9.5.52"/>
18+
<PackageReference Include="Argon.Sfu.Protocol" Version="1.26.0"/>
1919
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10">
2020
<PrivateAssets>all</PrivateAssets>
2121
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2222
</PackageReference>
23-
<PackageReference Include="Microsoft.Orleans.Persistence.AdoNet" Version="8.2.0" />
24-
<PackageReference Include="Microsoft.Orleans.Runtime" Version="8.2.0" />
25-
<PackageReference Include="Microsoft.Orleans.Sdk" Version="8.2.0" />
26-
<PackageReference Include="Microsoft.Orleans.Server" Version="8.2.0" />
27-
<PackageReference Include="Npgsql" Version="8.0.5" />
28-
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
29-
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.1.2" />
30-
23+
<PackageReference Include="Microsoft.Orleans.Persistence.AdoNet" Version="8.2.0"/>
24+
<PackageReference Include="Microsoft.Orleans.Runtime" Version="8.2.0"/>
25+
<PackageReference Include="Microsoft.Orleans.Sdk" Version="8.2.0"/>
26+
<PackageReference Include="Microsoft.Orleans.Serialization" Version="8.2.0"/>
27+
<PackageReference Include="Microsoft.Orleans.Server" Version="8.2.0"/>
28+
<PackageReference Include="Npgsql" Version="8.0.5"/>
29+
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
30+
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.1.2"/>
3131
</ItemGroup>
3232

3333
<ItemGroup>
34-
<ProjectReference Include="..\Argon.Contracts\Argon.Contracts.csproj" />
35-
<ProjectReference Include="..\ServiceDefaults\ServiceDefaults.csproj" />
34+
<ProjectReference Include="..\Argon.Contracts\Argon.Contracts.csproj"/>
35+
<ProjectReference Include="..\ServiceDefaults\ServiceDefaults.csproj"/>
3636
</ItemGroup>
3737

3838
<ItemGroup>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Argon.Api.Attributes;
2+
3+
[AttributeUsage(AttributeTargets.Method)]
4+
public class InjectUsernameAttribute : Attribute
5+
{
6+
}

src/Argon.Api/Controllers/UsersController.cs

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
namespace Argon.Api.Controllers;
22

3+
using Attributes;
34
using Grains.Interfaces;
45
using Grains.Persistence.States;
56
using Microsoft.AspNetCore.Authorization;
67
using Microsoft.AspNetCore.Mvc;
8+
using Sfu;
79

810
#if DEBUG
911
public record UserInputDto(string Username, string Password);
1012

13+
public record ServerInputDto(
14+
string Name,
15+
string Description);
16+
1117
[Route("api/[controller]")]
1218
public class UsersController(IGrainFactory grainFactory, ILogger<UsersController> logger) : ControllerBase
1319
{
@@ -28,19 +34,64 @@ public async Task<ActionResult<string>> Authenticate([FromBody] UserInputDto dto
2834

2935
[HttpGet]
3036
[Authorize]
31-
public async Task<ActionResult<UserStorageDto>> Get()
37+
[InjectUsername]
38+
public async Task<ActionResult<UserStorageDto>> Get(string username)
3239
{
33-
var username = User.Claims.FirstOrDefault(cl => cl.Type == "username")?.Value;
3440
var userManager = grainFactory.GetGrain<IUserManager>(username);
3541
return await userManager.Get();
3642
}
3743

3844
[HttpGet("{username}")]
39-
[Authorize]
40-
public async Task<ActionResult<UserStorageDto>> Get(string username)
45+
public async Task<ActionResult<UserStorageDto>> GetByUsername(string username)
4146
{
4247
var userManager = grainFactory.GetGrain<IUserManager>(username);
4348
return await userManager.Get();
4449
}
50+
51+
[HttpPost("server")]
52+
[Authorize]
53+
[InjectUsername]
54+
public async Task<ActionResult<ServerStorage>> CreateServer(string username, [FromBody] ServerInputDto dto)
55+
{
56+
var userManager = grainFactory.GetGrain<IUserManager>(username);
57+
return await userManager.CreateServer(dto.Name, dto.Description);
58+
}
59+
60+
[HttpGet("servers")]
61+
[Authorize]
62+
[InjectUsername]
63+
public async Task<ActionResult<List<ServerStorage>>> GetServers(string username)
64+
{
65+
var userManager = grainFactory.GetGrain<IUserManager>(username);
66+
return await userManager.GetServers();
67+
}
68+
69+
[HttpGet("servers/{serverId}/channels")]
70+
[Authorize]
71+
[InjectUsername]
72+
public async Task<ActionResult<IEnumerable<ChannelStorage>>> GetServerChannels(string username, Guid serverId)
73+
{
74+
var userManager = grainFactory.GetGrain<IUserManager>(username);
75+
var channels = await userManager.GetServerChannels(serverId);
76+
return channels.ToList();
77+
}
78+
79+
[HttpGet("servers/{serverId}/channels/{channelId}")]
80+
[Authorize]
81+
[InjectUsername]
82+
public async Task<ActionResult<ChannelStorage>> GetChannel(string username, Guid serverId, Guid channelId)
83+
{
84+
var userManager = grainFactory.GetGrain<IUserManager>(username);
85+
return await userManager.GetChannel(serverId, channelId);
86+
}
87+
88+
[HttpPost("servers/{serverId}/channels/{channelId}/join")]
89+
[Authorize]
90+
[InjectUsername]
91+
public async Task<ActionResult<RealtimeToken>> JoinChannel(string username, Guid serverId, Guid channelId)
92+
{
93+
var userManager = grainFactory.GetGrain<IUserManager>(username);
94+
return await userManager.JoinChannel(serverId, channelId);
95+
}
4596
}
4697
#endif

src/Argon.Api/Extensions/OrleansExtension.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
namespace Argon.Api.Extensions;
22

3+
using MemoryPack;
34
using Orleans.Configuration;
5+
using Orleans.Storage;
6+
7+
internal class MemoryPackStorageSerializer : IGrainStorageSerializer
8+
{
9+
public BinaryData Serialize<T>(T input)
10+
{
11+
return new BinaryData(MemoryPackSerializer.Serialize(input));
12+
}
13+
14+
public T Deserialize<T>(BinaryData input)
15+
{
16+
return MemoryPackSerializer.Deserialize<T>(input) ?? throw new InvalidOperationException();
17+
}
18+
}
419

520
public static class OrleansExtension
621
{
@@ -18,7 +33,9 @@ public static WebApplicationBuilder AddOrleans(this WebApplicationBuilder builde
1833
{
1934
options.Invariant = "Npgsql";
2035
options.ConnectionString = builder.Configuration.GetConnectionString("DefaultConnection");
36+
options.GrainStorageSerializer = new MemoryPackStorageSerializer();
2137
})
38+
.AddMemoryGrainStorageAsDefault()
2239
.UseLocalhostClustering();
2340
});
2441

src/Argon.Api/Features/Sfu/Models.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
namespace Argon.Sfu;
22

33
using LiveKit.Proto;
4+
using MemoryPack;
45

56
public record struct EphemeralChannelInfo(ArgonChannelId channelId, string sid, Room room);
67

7-
public record struct RealtimeToken(string value);
8+
[Serializable]
9+
[GenerateSerializer]
10+
[MemoryPackable]
11+
[Alias(nameof(RealtimeToken))]
12+
public partial record struct RealtimeToken(string value);
813

914
public record struct ArgonUserId(Guid id)
1015
{
11-
public string ToRawIdentity() => id.ToString("N");
16+
public string ToRawIdentity()
17+
{
18+
return id.ToString("N");
19+
}
1220
}
1321

1422
public record struct ArgonServerId(Guid id);
1523

1624
public record struct ArgonChannelId(ArgonServerId serverId, Guid channelId)
1725
{
18-
public string ToRawRoomId() => $"{serverId.id:N}:{channelId:N}";
26+
public string ToRawRoomId()
27+
{
28+
return $"{serverId.id:N}:{channelId:N}";
29+
}
1930
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Argon.Api.Filters;
2+
3+
using Microsoft.AspNetCore.Mvc.Filters;
4+
5+
public class InjectUsernameFilter : IActionFilter
6+
{
7+
public void OnActionExecuting(ActionExecutingContext context)
8+
{
9+
var username = context.HttpContext.User.Claims.FirstOrDefault(cl => cl.Type == "username")?.Value;
10+
if (!string.IsNullOrWhiteSpace(username))
11+
context.ActionArguments["username"] = username;
12+
}
13+
14+
public void OnActionExecuted(ActionExecutedContext context)
15+
{
16+
}
17+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace Argon.Api.Grains.Interfaces;
2+
3+
using Persistence.States;
4+
using Sfu;
5+
6+
public interface IChannelManager : IGrainWithGuidKey
7+
{
8+
[Alias("CreateChannel")]
9+
Task<ChannelStorage> CreateChannel(ChannelStorage channel);
10+
11+
[Alias("GetChannel")]
12+
Task<ChannelStorage> GetChannel();
13+
14+
[Alias("JoinLink")]
15+
Task<RealtimeToken> JoinLink(Guid userId, Guid serverId);
16+
17+
[Alias("UpdateChannel")]
18+
Task<ChannelStorage> UpdateChannel(ChannelStorage channel);
19+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace Argon.Api.Grains.Interfaces;
2+
3+
using Persistence.States;
4+
using Sfu;
5+
6+
public interface IServerManager : IGrainWithGuidKey
7+
{
8+
[Alias("CreateServer")]
9+
Task<ServerStorage> CreateServer(string name, string description, Guid userId);
10+
11+
[Alias("CreateJoinLink")]
12+
Task<string> CreateJoinLink();
13+
14+
[Alias("AddUser")]
15+
Task AddUser(UserToServerRelation Relation);
16+
17+
[Alias("GetChannels")]
18+
Task<IEnumerable<ChannelStorage>> GetChannels();
19+
20+
[Alias("AddChannel")]
21+
Task<ChannelStorage> AddChannel(ChannelStorage channel);
22+
23+
[Alias("GetChannel")]
24+
Task<ChannelStorage> GetChannel(Guid channelId);
25+
26+
[Alias("GetServer")]
27+
Task<ServerStorage> GetServer();
28+
29+
[Alias("JoinChannel")]
30+
Task<RealtimeToken> JoinChannel(Guid userId, Guid channelId);
31+
}

src/Argon.Api/Grains.Interfaces/IUserManager.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace Argon.Api.Grains.Interfaces;
22

33
using Persistence.States;
4+
using Sfu;
45

56
public interface IUserManager : IGrainWithStringKey
67
{
@@ -12,4 +13,19 @@ public interface IUserManager : IGrainWithStringKey
1213

1314
[Alias("Authenticate")]
1415
Task<string> Authenticate(string password);
16+
17+
[Alias("CreateServer")]
18+
Task<ServerStorage> CreateServer(string name, string description);
19+
20+
[Alias("GetServers")]
21+
Task<List<ServerStorage>> GetServers();
22+
23+
[Alias("GetServerChannels")]
24+
Task<IEnumerable<ChannelStorage>> GetServerChannels(Guid serverId);
25+
26+
[Alias("GetChannel")]
27+
Task<ChannelStorage> GetChannel(Guid serverId, Guid channelId);
28+
29+
[Alias("JoinChannel")]
30+
Task<RealtimeToken> JoinChannel(Guid serverId, Guid channelId);
1531
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace Argon.Api.Grains.Persistence.States;
2+
3+
using MemoryPack;
4+
5+
[GenerateSerializer]
6+
[Serializable]
7+
[MemoryPackable]
8+
[Alias(nameof(ChannelStorage))]
9+
public sealed partial record ChannelStorage
10+
{
11+
[Id(0)] public Guid Id { get; set; } = Guid.Empty;
12+
[Id(1)] public string Name { get; set; } = string.Empty;
13+
[Id(2)] public string Description { get; set; } = string.Empty;
14+
[Id(3)] public Guid CreatedBy { get; set; } = Guid.Empty;
15+
[Id(4)] public ChannelType ChannelType { get; set; } = ChannelType.Text;
16+
[Id(5)] public ServerRole AccessLevel { get; set; } = ServerRole.User;
17+
[Id(6)] public DateTime CreatedAt { get; } = DateTime.UtcNow;
18+
[Id(7)] public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
19+
}

0 commit comments

Comments
 (0)