Контракты

This commit is contained in:
Халимов Рустам
2026-03-29 23:39:17 +03:00
parent 22bc964f27
commit 0209802e9e
141 changed files with 1292 additions and 725 deletions
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,9 @@
using Microsoft.EntityFrameworkCore;
namespace Knot.Modules.Auth.Contracts.Application.Abstractions;
public interface IAuthDbContext
{
DbSet<T> Set<T>() where T : class;
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}
@@ -0,0 +1,11 @@
using Knot.Modules.Auth.Contracts.Domain;
using Knot.Shared.Kernel;
namespace Knot.Modules.Auth.Contracts.Application.Abstractions;
public interface IAuthUnitOfWork : IUnitOfWork
{
IUserRepository UserRepository { get; }
void Add(UserContract user);
Task<int> SaveChangesAsync(CancellationToken ct = default);
}
@@ -0,0 +1,9 @@
using Microsoft.EntityFrameworkCore;
namespace Knot.Modules.Auth.Contracts.Application.Abstractions;
public interface IIdentityDbContext
{
DbSet<T> Set<T>() where T : class;
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}
@@ -0,0 +1,9 @@
using Knot.Modules.Auth.Contracts.Domain;
using Knot.Shared.Kernel;
namespace Knot.Modules.Auth.Contracts.Application.Abstractions;
public interface IIdentityUnitOfWork : IUnitOfWork
{
IUserRepository UserRepository { get; }
}
@@ -0,0 +1,8 @@
namespace Knot.Modules.Auth.Contracts.Application.Abstractions;
public interface IJwtTokenProvider
{
string GenerateAccessToken(Guid userId, string username);
string GenerateRefreshToken();
string Generate(Guid userId, string username, string displayName, string? avatar);
}
@@ -0,0 +1,11 @@
using Knot.Shared.Kernel;
namespace Knot.Modules.Auth.Contracts.Application.Auth.DTOs;
public static class AuthErrors
{
public static Error IdentityInvalidCredentials => new("Auth.InvalidCredentials", "Invalid credentials");
public static Error IdentityRegistrationDisabled => new("Auth.RegistrationDisabled", "Registration is disabled");
public static Error IdentityUsernameNotUnique => new("Auth.UsernameNotUnique", "Username is already taken");
public static Error UserNotFound => new("Auth.UserNotFound", "User not found");
}
@@ -0,0 +1,16 @@
namespace Knot.Modules.Auth.Contracts.Application.Auth.DTOs;
public class AuthResponseDto
{
public required string AccessToken { get; set; }
public required string RefreshToken { get; set; }
public Guid UserId { get; set; }
public string Username { get; set; } = string.Empty;
public string? DisplayName { get; set; }
}
public class ResetPasswordDto
{
public required string Token { get; set; }
public required string NewPassword { get; set; }
}
@@ -0,0 +1,6 @@
using Knot.Modules.Auth.Contracts.Domain;
using MediatR;
namespace Knot.Modules.Auth.Contracts.Application.Users.GetMe;
public record GetMeQuery() : IRequest<UserContract>;
@@ -0,0 +1,10 @@
using Knot.Modules.Auth.Contracts.Application.Auth.DTOs;
using Knot.Shared.Kernel;
using MediatR;
namespace Knot.Modules.Auth.Contracts.Application.Users.Login;
public record LoginUserCommand(
string Username,
string Password
) : IRequest<Result<AuthResponseDto>>;
@@ -0,0 +1,14 @@
using Knot.Modules.Auth.Contracts.Application.Auth.DTOs;
using Knot.Modules.Auth.Contracts.Domain;
using Knot.Shared.Kernel;
using MediatR;
namespace Knot.Modules.Auth.Contracts.Application.Users.Register;
public record RegisterUserCommand(
string Username,
string Password,
string DisplayName,
string? PhoneNumber,
bool IsBot = false
) : IRequest<Result<AuthResponseDto>>;
@@ -0,0 +1,16 @@
namespace Knot.Modules.Auth.Contracts.Domain;
public interface IUserRepository
{
Task<UserContract?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<List<UserContract>> GetByIdsAsync(IEnumerable<Guid> ids, CancellationToken cancellationToken = default);
Task<UserContract?> GetByUsernameAsync(string username, CancellationToken cancellationToken = default);
Task<bool> IsUsernameUniqueAsync(string username, CancellationToken cancellationToken = default);
Task<List<UserContract>> SearchUsersAsync(string query, CancellationToken cancellationToken = default);
Task<bool> IsBannedAsync(Guid userId, CancellationToken cancellationToken = default);
Task<UserContract?> GetByRefreshTokenAsync(string refreshToken, CancellationToken cancellationToken = default);
Task UpdateAsync(UserContract user, CancellationToken cancellationToken = default);
void Add(UserContract user);
Task RemoveAsync(UserContract user, CancellationToken cancellationToken = default);
void Update(UserContract user);
}
@@ -0,0 +1,22 @@
namespace Knot.Modules.Auth.Contracts.Domain;
public class UserContract
{
public Guid Id { get; set; }
public string Username { get; set; } = string.Empty;
public string DisplayName { get; set; } = string.Empty;
public string PasswordHash { get; set; } = string.Empty;
public string? PhoneNumber { get; set; }
public string? Email { get; set; }
public string? Bio { get; set; }
public string? Avatar { get; set; }
public DateTime? Birthday { get; set; }
public bool IsBot { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? BannedUntil { get; set; }
public bool IsBanned { get; set; }
public bool IsOnline { get; set; }
public bool IsExternal { get; set; }
public string? Domain { get; set; }
public DateTime? LastSeen { get; set; }
}
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.4" />
</ItemGroup>
</Project>
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,12 @@
using Knot.Shared.Kernel;
namespace Knot.Modules.Profiles.Contracts.Application.DTOs;
public static class ProfilesErrors
{
public static Error ProfileNotFound => new("Profiles.NotFound", "Profile not found");
public static Error UsernameAlreadyExists => new("Profiles.UsernameExists", "Username already exists");
public static Error AvatarNotFound => new("Profiles.AvatarNotFound", "Avatar not found");
public static Error InvalidAvatarFormat => new("Profiles.InvalidAvatarFormat", "Invalid avatar format");
public static Error AvatarUploadFailed => new("Profiles.AvatarUploadFailed", "Avatar upload failed");
}
@@ -0,0 +1,21 @@
using MediatR;
namespace Knot.Modules.Profiles.Contracts.Application.DTOs;
public record SearchProfilesRequest(string Query, int Limit = 20) : IRequest<List<UserProfileDto>>;
public record UpdateProfileRequest(
string? DisplayName,
string? About,
string? Avatar,
bool RemoveAvatar = false
) : IRequest<UserProfileDto>;
public record UpdateSettingsRequest(
bool? ShowLastSeen,
bool? ShowAvatar,
bool? ShowAbout,
bool? ShowPhoneNumber,
bool? ShowForwardedMessages,
bool? ShowAddToGroupPermission
) : IRequest<UserProfileDto>;
@@ -0,0 +1,14 @@
namespace Knot.Modules.Profiles.Contracts.Application.DTOs;
public class UserProfileDto
{
public Guid UserId { get; set; }
public string? DisplayName { get; set; }
public string? Username { get; set; }
public string? About { get; set; }
public string? Avatar { get; set; }
public bool IsBot { get; set; }
public DateTime? LastSeen { get; set; }
public bool IsPremium { get; set; }
public DateTime CreatedAt { get; set; }
}
@@ -0,0 +1,10 @@
using Knot.Shared.Kernel;
using MediatR;
namespace Knot.Modules.Profiles.Contracts.Application.Profiles.Avatar;
public record UploadAvatarCommand(Stream Stream, string FileName) : IRequest<Result<string>>;
public record DeleteAvatarCommand() : IRequest<Result>;
public record CropAvatarCommand(int X, int Y, int Width, int Height) : IRequest<Result<string>>;
@@ -0,0 +1,9 @@
using Knot.Modules.Profiles.Contracts.Application.DTOs;
using Knot.Shared.Kernel;
using MediatR;
namespace Knot.Modules.Profiles.Contracts.Application.Profiles.GetUser;
public record GetProfileQuery(Guid UserId) : IRequest<Result<UserProfileDto>>;
public record GetCurrentUserQuery() : IRequest<Result<UserProfileDto>>;
@@ -0,0 +1,6 @@
using Knot.Modules.Profiles.Contracts.Application.DTOs;
using MediatR;
namespace Knot.Modules.Profiles.Contracts.Application.Profiles.Search;
public record SearchProfilesQuery(string Query, int Limit = 20) : IRequest<List<UserProfileDto>>;
@@ -0,0 +1,12 @@
using Knot.Modules.Profiles.Contracts.Application.DTOs;
using Knot.Shared.Kernel;
using MediatR;
namespace Knot.Modules.Profiles.Contracts.Application.Profiles.UpdateProfile;
public record UpdateProfileCommand(
string? DisplayName,
string? About,
string? Avatar,
bool RemoveAvatar = false
) : IRequest<Result<UserProfileDto>>;
@@ -0,0 +1,14 @@
using Knot.Modules.Profiles.Contracts.Application.DTOs;
using Knot.Shared.Kernel;
using MediatR;
namespace Knot.Modules.Profiles.Contracts.Application.Profiles.UpdateSettings;
public record UpdateSettingsCommand(
bool? ShowLastSeen,
bool? ShowAvatar,
bool? ShowAbout,
bool? ShowPhoneNumber,
bool? ShowForwardedMessages,
bool? ShowAddToGroupPermission
) : IRequest<Result<UserProfileDto>>;
@@ -0,0 +1,11 @@
using Knot.Shared.Kernel.Storage;
namespace Knot.Modules.Profiles.Contracts.Domain;
public interface IAvatarStorageService
{
Task<string> UploadAsync(Stream stream, string fileName, string contentType, CancellationToken cancellationToken = default);
Task DeleteAsync(string fileKey, CancellationToken cancellationToken = default);
Task<Stream?> GetAsync(string fileKey, CancellationToken cancellationToken = default);
string GetFileUrl(string fileKey);
}
@@ -0,0 +1,15 @@
using Knot.Modules.Profiles.Contracts.Application.DTOs;
using Knot.Shared.Kernel;
namespace Knot.Modules.Profiles.Contracts.Domain;
public interface IProfileRepository
{
Task<UserProfileDto?> GetAsync(Guid userId, CancellationToken cancellationToken = default);
Task<UserProfileDto?> GetByUsernameAsync(string username, CancellationToken cancellationToken = default);
Task<List<UserProfileDto>> GetAsync(IEnumerable<Guid> userIds, CancellationToken cancellationToken = default);
Task<List<UserProfileDto>> SearchAsync(string query, int limit = 20, CancellationToken cancellationToken = default);
Task<Result<UserProfileDto>> CreateAsync(UserProfileDto dto, CancellationToken cancellationToken = default);
Task<Result<UserProfileDto>> UpdateAsync(UserProfileDto dto, CancellationToken cancellationToken = default);
Task<Result> DeleteAsync(Guid userId, CancellationToken cancellationToken = default);
}
@@ -0,0 +1,8 @@
using Knot.Shared.Kernel;
namespace Knot.Modules.Profiles.Contracts.Domain;
public interface IProfilesUnitOfWork : IUnitOfWork
{
IProfileRepository ProfileRepository { get; }
}
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,50 @@
using Knot.Modules.Settings.Contracts.Application.DTOs;
namespace Knot.Modules.Settings.Contracts.Application.Abstractions;
public interface ISettingsService
{
Task<SystemSettingsDto> GetSettingsAsync(CancellationToken cancellationToken = default);
Task UpdateSettingsAsync(SystemSettingsDto settings, CancellationToken cancellationToken = default);
SystemSettingsDto Current { get; }
}
public interface ISystemSettings
{
SystemConfig Current { get; }
}
public interface IStoriesSettings
{
StoriesConfig Current { get; }
}
public interface IChatsSettings
{
ChatsConfig Current { get; }
}
public interface IMessagesSettings
{
MessagesConfig Current { get; }
}
public interface IWebRtcSettings
{
WebRtcConfig Current { get; }
}
public interface IKlipySettings
{
KlipyConfig Current { get; }
}
public interface IImportSettings
{
ImportConfig Current { get; }
}
public interface IFederationSettings
{
FederationConfig Current { get; }
}
@@ -0,0 +1,112 @@
using System.Collections.Generic;
namespace Knot.Modules.Settings.Contracts.Application.DTOs;
public class SystemConfig
{
public string ServerTimezone { get; set; } = "UTC";
public string DomainUrl { get; set; } = "https://example.com";
public string AdminRoute { get; set; } = "admin";
public bool EnableRegistration { get; set; } = true;
}
public class StoriesConfig
{
public bool Enabled { get; set; } = true;
public int MaxStoriesPerPeriod { get; set; } = 5;
public int StoryLifetimeHours { get; set; } = 24;
public bool TextStoriesEnabled { get; set; } = true;
public int TextStoryDurationSeconds { get; set; } = 15;
public int MediaStoryMaxDurationSeconds { get; set; } = 30;
public int MaxMediaSizeBytes { get; set; } = 15 * 1024 * 1024;
}
public class ChatsConfig
{
public bool SupportGroups { get; set; } = true;
public int MaxGroupParticipants { get; set; } = 200000;
public bool EnableAutoClean { get; set; } = false;
public bool AllowChatToGroupConversion { get; set; } = true;
public bool EnableFolders { get; set; } = true;
}
public class MessagesConfig
{
public int DailyMessageLimitPerUser { get; set; } = 0;
public int ChatMessageLimit { get; set; } = 0;
public bool AllowMedia { get; set; } = true;
public int MaxMediaSizeBytes { get; set; } = 50 * 1024 * 1024;
public int MaxFileSize { get; set; } = 100 * 1024 * 1024;
public List<string> AllowedMediaTypes { get; set; } = new() { "image/jpeg", "image/png", "video/mp4", "image/gif" };
public bool AllowVoiceMessages { get; set; } = true;
public bool AllowForwarding { get; set; } = true;
public bool AllowReactions { get; set; } = true;
public bool AllowReplies { get; set; } = true;
public bool AllowQuoting { get; set; } = true;
public bool AllowMessageDeletion { get; set; } = true;
public bool ForbidCopying { get; set; } = false;
public bool AllowLinks { get; set; } = true;
public bool AllowPolls { get; set; } = true;
public bool AllowPinning { get; set; } = true;
}
public class WebRtcConfig
{
public bool Enabled { get; set; } = false;
public bool EnableVideoCalls { get; set; } = true;
public bool EnableScreenSharing { get; set; } = true;
public string TurnHost { get; set; } = string.Empty;
public int TurnPort { get; set; } = 3478;
public string TurnUser { get; set; } = string.Empty;
public string TurnSecret { get; set; } = string.Empty;
}
public class KlipyConfig
{
public bool Enabled { get; set; } = false;
public string ApiKey { get; set; } = string.Empty;
public string AppName { get; set; } = string.Empty;
}
public class ImportConfig
{
public bool EnableTelegramImport { get; set; } = false;
}
public class FederationDomainConfig
{
public string Domain { get; set; } = string.Empty;
public bool IsEnabled { get; set; } = true;
public string? PublicKey { get; set; }
public RemoteCapabilities? Capabilities { get; set; }
}
public class RemoteCapabilities
{
public bool AllowMedia { get; set; }
public bool AllowPolls { get; set; }
public bool AllowVoiceMessages { get; set; }
public bool AllowVideoCalls { get; set; }
public bool AllowScreenSharing { get; set; }
}
public class FederationConfig
{
public bool Enabled { get; set; } = false;
public string ServerDescription { get; set; } = string.Empty;
public string? PrivateKey { get; set; }
public string? PublicKey { get; set; }
public List<FederationDomainConfig> AllowedDomains { get; set; } = new();
}
public class SystemSettingsDto
{
public SystemConfig System { get; set; } = new();
public StoriesConfig Stories { get; set; } = new();
public ChatsConfig Chats { get; set; } = new();
public MessagesConfig Messages { get; set; } = new();
public WebRtcConfig WebRtc { get; set; } = new();
public KlipyConfig Klipy { get; set; } = new();
public ImportConfig Import { get; set; } = new();
public FederationConfig Federation { get; set; } = new();
}
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.0.1" />
</ItemGroup>
</Project>