diff --git a/src/Argon.Api/Argon.Api.csproj b/src/Argon.Api/Argon.Api.csproj
index e1d0b5d3..cfcf0e9e 100644
--- a/src/Argon.Api/Argon.Api.csproj
+++ b/src/Argon.Api/Argon.Api.csproj
@@ -11,6 +11,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/src/Argon.Api/Controllers/UsersController.cs b/src/Argon.Api/Controllers/UsersController.cs
index 4c0abbb0..9c2e35f9 100644
--- a/src/Argon.Api/Controllers/UsersController.cs
+++ b/src/Argon.Api/Controllers/UsersController.cs
@@ -28,7 +28,7 @@ public async Task> Post([FromBody] UserInputDto dto
public async Task> Authenticate([FromBody] UserInputDto dto)
{
var userManager = grainFactory.GetGrain(dto.Username);
- var token = await userManager.Authenticate(dto.Password);
+ var token = await userManager.Authorize(dto.Password);
return token;
}
diff --git a/src/Argon.Api/Grains.Interfaces/IUserManager.cs b/src/Argon.Api/Grains.Interfaces/IUserManager.cs
index 59991259..2829a54a 100644
--- a/src/Argon.Api/Grains.Interfaces/IUserManager.cs
+++ b/src/Argon.Api/Grains.Interfaces/IUserManager.cs
@@ -11,8 +11,8 @@ public interface IUserManager : IGrainWithStringKey
[Alias("Get")]
Task Get();
- [Alias("Authenticate")]
- Task Authenticate(string password);
+ [Alias("Authorize")]
+ Task Authorize(string password);
[Alias("CreateServer")]
Task CreateServer(string name, string description);
diff --git a/src/Argon.Api/Grains.Persistence.States/ChannelStorage.cs b/src/Argon.Api/Grains.Persistence.States/ChannelStorage.cs
index 8bf7fbb1..52295b87 100644
--- a/src/Argon.Api/Grains.Persistence.States/ChannelStorage.cs
+++ b/src/Argon.Api/Grains.Persistence.States/ChannelStorage.cs
@@ -1,5 +1,6 @@
namespace Argon.Api.Grains.Persistence.States;
+using Contracts;
using MemoryPack;
[GenerateSerializer]
@@ -16,4 +17,18 @@ public sealed partial record ChannelStorage
[Id(5)] public ServerRole AccessLevel { get; set; } = ServerRole.User;
[Id(6)] public DateTime CreatedAt { get; } = DateTime.UtcNow;
[Id(7)] public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
+
+ public static implicit operator ServerDetailsResponse(ChannelStorage channelStorage)
+ {
+ return new ServerDetailsResponse(
+ channelStorage.Id,
+ channelStorage.Name,
+ channelStorage.Description,
+ channelStorage.CreatedBy.ToString("N"),
+ channelStorage.ChannelType.ToString(),
+ channelStorage.AccessLevel.ToString(),
+ channelStorage.CreatedAt,
+ channelStorage.UpdatedAt
+ );
+ }
}
\ No newline at end of file
diff --git a/src/Argon.Api/Grains.Persistence.States/ServerStorage.cs b/src/Argon.Api/Grains.Persistence.States/ServerStorage.cs
index d517a3e0..0f16794b 100644
--- a/src/Argon.Api/Grains.Persistence.States/ServerStorage.cs
+++ b/src/Argon.Api/Grains.Persistence.States/ServerStorage.cs
@@ -1,5 +1,6 @@
namespace Argon.Api.Grains.Persistence.States;
+using Contracts;
using MemoryPack;
[GenerateSerializer]
@@ -15,4 +16,17 @@ public sealed partial record ServerStorage
[Id(6)] public List Channels { get; set; } = [];
[Id(3)] public DateTime CreatedAt { get; } = DateTime.UtcNow;
[Id(4)] public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
+
+ public static implicit operator ServerResponse(ServerStorage serverStorage)
+ {
+ return new ServerResponse(
+ serverStorage.Id,
+ serverStorage.Name,
+ serverStorage.Description,
+ serverStorage.AvatarUrl,
+ serverStorage.Channels,
+ serverStorage.CreatedAt,
+ serverStorage.UpdatedAt
+ );
+ }
}
\ No newline at end of file
diff --git a/src/Argon.Api/Grains.Persistence.States/UserStorage.cs b/src/Argon.Api/Grains.Persistence.States/UserStorage.cs
index 09637aea..0f8eb863 100644
--- a/src/Argon.Api/Grains.Persistence.States/UserStorage.cs
+++ b/src/Argon.Api/Grains.Persistence.States/UserStorage.cs
@@ -1,6 +1,8 @@
namespace Argon.Api.Grains.Persistence.States;
+using System.Runtime.Serialization;
using MemoryPack;
+using MessagePack;
[GenerateSerializer]
[Serializable]
@@ -8,12 +10,41 @@ namespace Argon.Api.Grains.Persistence.States;
[Alias(nameof(UserStorage))]
public sealed partial record UserStorage
{
- [Id(0)] public Guid Id { get; set; } = Guid.Empty;
- [Id(1)] public string Username { get; set; } = string.Empty;
- [Id(2)] public string Password { get; set; } = string.Empty;
- [Id(5)] public string AvatarUrl { get; set; } = string.Empty;
- [Id(3)] public DateTime CreatedAt { get; } = DateTime.UtcNow;
- [Id(4)] public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
+ [property: DataMember(Order = 0)]
+ [property: MemoryPackOrder(0)]
+ [property: Key(0)]
+ [Id(0)]
+ public Guid Id { get; set; } = Guid.Empty;
+
+ [property: DataMember(Order = 1)]
+ [property: MemoryPackOrder(1)]
+ [property: Key(1)]
+ [Id(1)]
+ public string Username { get; set; } = string.Empty;
+
+ [property: DataMember(Order = 2)]
+ [property: MemoryPackOrder(2)]
+ [property: Key(2)]
+ [Id(2)]
+ public string Password { get; set; } = string.Empty;
+
+ [property: DataMember(Order = 5)]
+ [property: MemoryPackOrder(5)]
+ [property: Key(5)]
+ [Id(5)]
+ public string AvatarUrl { get; set; } = string.Empty;
+
+ [property: DataMember(Order = 3)]
+ [property: MemoryPackOrder(3)]
+ [property: Key(3)]
+ [Id(3)]
+ public DateTime CreatedAt { get; } = DateTime.UtcNow;
+
+ [property: DataMember(Order = 4)]
+ [property: MemoryPackOrder(4)]
+ [property: Key(4)]
+ [Id(4)]
+ public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public static implicit operator UserStorageDto(UserStorage userStorage)
{
diff --git a/src/Argon.Api/Grains.Persistence.States/UserStorageDto.cs b/src/Argon.Api/Grains.Persistence.States/UserStorageDto.cs
index 72d03c47..32b44554 100644
--- a/src/Argon.Api/Grains.Persistence.States/UserStorageDto.cs
+++ b/src/Argon.Api/Grains.Persistence.States/UserStorageDto.cs
@@ -1,11 +1,14 @@
namespace Argon.Api.Grains.Persistence.States;
+using Contracts;
+using Mapster;
using MemoryPack;
[GenerateSerializer]
[Serializable]
[MemoryPackable]
[Alias(nameof(UserStorageDto))]
+[GenerateMapper]
public sealed partial record UserStorageDto
{
[Id(0)] public Guid Id { get; set; }
@@ -13,4 +16,15 @@ public sealed partial record UserStorageDto
[Id(4)] public string AvatarUrl { get; set; } = string.Empty;
[Id(2)] public DateTime CreatedAt { get; set; }
[Id(3)] public DateTime UpdatedAt { get; set; }
+
+ public static implicit operator UserResponse(UserStorageDto userStorageDto)
+ {
+ return new UserResponse(
+ userStorageDto.Id,
+ userStorageDto.Username,
+ userStorageDto.AvatarUrl,
+ userStorageDto.CreatedAt,
+ userStorageDto.UpdatedAt
+ );
+ }
}
\ No newline at end of file
diff --git a/src/Argon.Api/Grains/UserManager.cs b/src/Argon.Api/Grains/UserManager.cs
index 6193ded6..0ec76647 100644
--- a/src/Argon.Api/Grains/UserManager.cs
+++ b/src/Argon.Api/Grains/UserManager.cs
@@ -36,7 +36,7 @@ public async Task Get()
return userStore.State;
}
- public Task Authenticate(string password)
+ public Task Authorize(string password)
{
var match = userStore.State.Password == HashPassword(password);
diff --git a/src/Argon.Api/Program.cs b/src/Argon.Api/Program.cs
index d3d4bc06..343dfc02 100644
--- a/src/Argon.Api/Program.cs
+++ b/src/Argon.Api/Program.cs
@@ -18,6 +18,7 @@
builder.Services.AddControllers(opts => { opts.Filters.Add(); });
builder.Services.AddFusion(RpcServiceMode.Server, true)
.Rpc.AddServer()
+ .AddServer()
.AddWebSocketServer(true);
builder.AddSwaggerWithAuthHeader();
builder.AddJwt();
diff --git a/src/Argon.Api/Services/UserAuthorization.cs b/src/Argon.Api/Services/UserAuthorization.cs
index 03aac0ba..26141e82 100644
--- a/src/Argon.Api/Services/UserAuthorization.cs
+++ b/src/Argon.Api/Services/UserAuthorization.cs
@@ -1,14 +1,14 @@
namespace Argon.Api.Services;
-using Grains.Interfaces;
using Contracts;
+using Grains.Interfaces;
public class UserAuthorization(IGrainFactory grainFactory) : IUserAuthorization
{
public async Task AuthorizeAsync(AuthorizeRequest request)
{
// TODO machineKey
- var token = await grainFactory.GetGrain(request.username).Authenticate(request.password);
- return new AuthorizeResponse(token, [new ServerResponse(Guid.NewGuid(), "xuita", null)]);
+ var token = await grainFactory.GetGrain(request.username).Authorize(request.password);
+ return new AuthorizeResponse(token);
}
}
\ No newline at end of file
diff --git a/src/Argon.Api/Services/UserInteractionService.cs b/src/Argon.Api/Services/UserInteractionService.cs
new file mode 100644
index 00000000..85798eb6
--- /dev/null
+++ b/src/Argon.Api/Services/UserInteractionService.cs
@@ -0,0 +1,41 @@
+namespace Argon.Api.Services;
+
+using Contracts;
+using Grains.Interfaces;
+
+public class UserInteractionService(
+ string username, // TODO to be injected
+ IGrainFactory grainFactory
+) : IUserInteraction
+{
+ private readonly IUserManager userManager = grainFactory.GetGrain(username);
+
+ public async Task GetMe()
+ {
+ return await userManager.Get();
+ }
+
+ public async Task CreateServer(CreateServerRequest request)
+ {
+ return await userManager.CreateServer(request.Name, request.Description);
+ }
+
+ public async Task> GetServers()
+ {
+ return (await userManager.GetServers())
+ .Select(x => (ServerResponse)x)
+ .ToList();
+ }
+
+ public async Task> GetServerDetails(ServerDetailsRequest request)
+ {
+ return (await userManager.GetServerChannels(request.ServerId))
+ .Select(x => (ServerDetailsResponse)x)
+ .ToList();
+ }
+
+ public async Task JoinChannel(ChannelJoinRequest request)
+ {
+ return new ChannelJoinResponse((await userManager.JoinChannel(request.ServerId, request.ChannelId)).value);
+ }
+}
\ No newline at end of file
diff --git a/src/Argon.Contracts/Argon.Contracts.csproj b/src/Argon.Contracts/Argon.Contracts.csproj
index cd6dcee5..6f90131f 100644
--- a/src/Argon.Contracts/Argon.Contracts.csproj
+++ b/src/Argon.Contracts/Argon.Contracts.csproj
@@ -1,22 +1,23 @@
-
- net8.0
- enable
- enable
-
+
+ net8.0
+ enable
+ enable
+
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/src/Argon.Contracts/IUserAuthorization.cs b/src/Argon.Contracts/IUserAuthorization.cs
index abcda690..0404cf4d 100644
--- a/src/Argon.Contracts/IUserAuthorization.cs
+++ b/src/Argon.Contracts/IUserAuthorization.cs
@@ -1,7 +1,6 @@
namespace Argon.Contracts;
using System.Runtime.Serialization;
-using ActualLab.Fusion;
using ActualLab.Rpc;
using MemoryPack;
using MessagePack;
@@ -11,19 +10,25 @@ public interface IUserAuthorization : IRpcService
Task AuthorizeAsync(AuthorizeRequest request);
}
-[DataContract, MemoryPackable(GenerateType.VersionTolerant), MessagePackObject]
+[DataContract]
+[MemoryPackable(GenerateType.VersionTolerant)]
+[MessagePackObject]
public sealed partial record AuthorizeRequest(
- [property: DataMember(Order = 0), MemoryPackOrder(0), Key(0)] string username,
- [property: DataMember(Order = 1), MemoryPackOrder(1), Key(1)] string password,
- [property: DataMember(Order = 2), MemoryPackOrder(2), Key(2)] string machineKey);
+ [property: DataMember(Order = 0)]
+ [property: MemoryPackOrder(0)]
+ [property: Key(0)]
+ string username,
+ [property: DataMember(Order = 1)]
+ [property: MemoryPackOrder(1)]
+ [property: Key(1)]
+ string password,
+ [property: DataMember(Order = 2)]
+ [property: MemoryPackOrder(2)]
+ [property: Key(2)]
+ string machineKey);
-
-public sealed partial record ServerResponse(
- [property: DataMember(Order = 0), MemoryPackOrder(0), Key(0)] Guid serverId,
- [property: DataMember(Order = 1), MemoryPackOrder(1), Key(1)] string serverName,
- [property: DataMember(Order = 2), MemoryPackOrder(2), Key(2)] string? avatarFileId
-);
-
-public sealed partial record AuthorizeResponse(
- [property: DataMember(Order = 0), MemoryPackOrder(0), Key(0)] string token,
- [property: DataMember(Order = 1), MemoryPackOrder(1), Key(1)] List servers);
\ No newline at end of file
+public sealed record AuthorizeResponse(
+ [property: DataMember(Order = 0)]
+ [property: MemoryPackOrder(0)]
+ [property: Key(0)]
+ string token);
\ No newline at end of file
diff --git a/src/Argon.Contracts/IUserInteraction.cs b/src/Argon.Contracts/IUserInteraction.cs
new file mode 100644
index 00000000..39fad3dc
--- /dev/null
+++ b/src/Argon.Contracts/IUserInteraction.cs
@@ -0,0 +1,170 @@
+namespace Argon.Contracts;
+
+using System.Runtime.Serialization;
+using ActualLab.Rpc;
+using MemoryPack;
+using MessagePack;
+
+public interface IUserInteraction : IRpcService
+{
+ Task GetMe();
+ Task CreateServer(CreateServerRequest request);
+ Task> GetServers();
+
+ Task> GetServerDetails(ServerDetailsRequest request);
+
+ // Task CreateChannel(string username);
+ Task JoinChannel(ChannelJoinRequest request);
+}
+
+[DataContract]
+[MemoryPackable(GenerateType.VersionTolerant)]
+[MessagePackObject]
+public sealed partial record ServerDetailsRequest(
+ [property: DataMember(Order = 0)]
+ [property: MemoryPackOrder(0)]
+ [property: Key(0)]
+ Guid ServerId
+);
+
+[DataContract]
+[MemoryPackable(GenerateType.VersionTolerant)]
+[MessagePackObject]
+public sealed partial record UserResponse(
+ [property: DataMember(Order = 0)]
+ [property: MemoryPackOrder(0)]
+ [property: Key(0)]
+ Guid Id,
+ [property: DataMember(Order = 1)]
+ [property: MemoryPackOrder(1)]
+ [property: Key(1)]
+ string Username,
+ [property: DataMember(Order = 2)]
+ [property: MemoryPackOrder(2)]
+ [property: Key(2)]
+ string AvatarUrl,
+ [property: DataMember(Order = 3)]
+ [property: MemoryPackOrder(3)]
+ [property: Key(3)]
+ DateTime CreatedAt,
+ [property: DataMember(Order = 4)]
+ [property: MemoryPackOrder(4)]
+ [property: Key(4)]
+ DateTime UpdatedAt
+);
+
+[DataContract]
+[MemoryPackable(GenerateType.VersionTolerant)]
+[MessagePackObject]
+public sealed partial record CreateServerRequest(
+ [property: DataMember(Order = 0)]
+ [property: MemoryPackOrder(0)]
+ [property: Key(0)]
+ string Name,
+ [property: DataMember(Order = 1)]
+ [property: MemoryPackOrder(1)]
+ [property: Key(1)]
+ string Description,
+ [property: DataMember(Order = 2)]
+ [property: MemoryPackOrder(2)]
+ [property: Key(2)]
+ string AvatarUrl
+);
+
+[DataContract]
+[MemoryPackable(GenerateType.VersionTolerant)]
+[MessagePackObject]
+public sealed partial record ServerResponse(
+ [property: DataMember(Order = 0)]
+ [property: MemoryPackOrder(0)]
+ [property: Key(0)]
+ Guid Id,
+ [property: DataMember(Order = 1)]
+ [property: MemoryPackOrder(1)]
+ [property: Key(1)]
+ string Name,
+ [property: DataMember(Order = 2)]
+ [property: MemoryPackOrder(2)]
+ [property: Key(2)]
+ string Description,
+ [property: DataMember(Order = 3)]
+ [property: MemoryPackOrder(3)]
+ [property: Key(3)]
+ string AvatarUrl,
+ [property: DataMember(Order = 4)]
+ [property: MemoryPackOrder(4)]
+ [property: Key(4)]
+ List Channels,
+ [property: DataMember(Order = 5)]
+ [property: MemoryPackOrder(5)]
+ [property: Key(5)]
+ DateTime CreatedAt,
+ [property: DataMember(Order = 6)]
+ [property: MemoryPackOrder(6)]
+ [property: Key(6)]
+ DateTime UpdatedAt
+ // TODO: all users of the server with their statuses(online/offline/in channel)
+);
+
+[DataContract]
+[MemoryPackable(GenerateType.VersionTolerant)]
+[MessagePackObject]
+public sealed partial record ServerDetailsResponse(
+ [property: DataMember(Order = 0)]
+ [property: MemoryPackOrder(0)]
+ [property: Key(0)]
+ Guid Id,
+ [property: DataMember(Order = 1)]
+ [property: MemoryPackOrder(1)]
+ [property: Key(1)]
+ string Name,
+ [property: DataMember(Order = 2)]
+ [property: MemoryPackOrder(2)]
+ [property: Key(2)]
+ string Description,
+ [property: DataMember(Order = 3)]
+ [property: MemoryPackOrder(3)]
+ [property: Key(3)]
+ string CreatedBy,
+ [property: DataMember(Order = 4)]
+ [property: MemoryPackOrder(4)]
+ [property: Key(4)]
+ string ChannelType,
+ [property: DataMember(Order = 5)]
+ [property: MemoryPackOrder(5)]
+ [property: Key(5)]
+ string AccessLevel,
+ [property: DataMember(Order = 6)]
+ [property: MemoryPackOrder(6)]
+ [property: Key(6)]
+ DateTime CreatedAt,
+ [property: DataMember(Order = 7)]
+ [property: MemoryPackOrder(7)]
+ [property: Key(7)]
+ DateTime UpdatedAt
+ // TODO: all users currently connected to this channel
+);
+
+[DataContract]
+[MemoryPackable(GenerateType.VersionTolerant)]
+[MessagePackObject]
+public sealed partial record ChannelJoinRequest(
+ [property: DataMember(Order = 0)]
+ [property: MemoryPackOrder(0)]
+ [property: Key(0)]
+ Guid ServerId,
+ [property: DataMember(Order = 1)]
+ [property: MemoryPackOrder(1)]
+ [property: Key(1)]
+ Guid ChannelId
+);
+
+[DataContract]
+[MemoryPackable(GenerateType.VersionTolerant)]
+[MessagePackObject]
+public sealed partial record ChannelJoinResponse(
+ [property: DataMember(Order = 0)]
+ [property: MemoryPackOrder(0)]
+ [property: Key(0)]
+ string Token
+);
\ No newline at end of file