Эндпоинты

This commit is contained in:
Халимов Рустам
2026-03-30 23:41:01 +03:00
parent ce212c11c1
commit d3f1e3f361
50 changed files with 1890 additions and 5 deletions
@@ -0,0 +1,14 @@
using System.Threading;
using System.Threading.Tasks;
using Knot.Modules.Auth.Domain;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace Knot.Modules.Auth.Application.Abstractions;
public interface IAuthDbContext
{
DbSet<User> Users { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}
@@ -0,0 +1,11 @@
using Knot.Shared.Kernel;
namespace Knot.Modules.Auth.Application.Abstractions;
/// <summary>
/// Unit of Work специфичный для модуля Identity.
/// </summary>
public interface IAuthUnitOfWork : IUnitOfWork
{
}
@@ -0,0 +1,9 @@
using Knot.Modules.Auth.Domain;
namespace Knot.Modules.Auth.Application.Abstractions;
public interface IJwtTokenProvider
{
string Generate(User user);
}
@@ -0,0 +1,21 @@
using System;
namespace Knot.Modules.Auth.Application.Auth.DTOs;
public record AuthResponseDto(
string Token,
AuthUserDto User
);
public record AuthUserDto(
Guid Id,
string Username,
string DisplayName,
string? Email,
string? Bio,
string? Avatar,
DateTime? Birthday,
bool IsOnline,
DateTime CreatedAt
);
@@ -0,0 +1,21 @@
using System;
namespace Knot.Modules.Auth.Application.Users.Auth;
public record AuthResponseDto(
string Token,
AuthUserDto User
);
public record AuthUserDto(
Guid Id,
string Username,
string DisplayName,
string? Email,
string? Bio,
string? Avatar,
DateTime? Birthday,
bool IsOnline,
DateTime CreatedAt
);
@@ -0,0 +1,15 @@
using Knot.Shared.Kernel;
namespace Knot.Modules.Auth.Domain;
public static class AuthErrors
{
public static readonly Error FriendsNotFound = new Error("Friends.NotFound", "Friendship not found");
public static readonly Error FriendsSelf = new Error("Friends.Self", "Cannot add yourself");
public static readonly Error FriendsExists = new Error("Friends.Exists", "Friendship already exists");
public static readonly Error UserNotFound = new Error("User.NotFound", "User not found");
public static readonly Error IdentityInvalidCredentials = new Error("Identity.InvalidCredentials", "Неверное имя пользователя или пароль.");
public static readonly Error IdentityRegistrationDisabled = new Error("Identity.RegistrationDisabled", "Registration is disabled by the administrator.");
public static readonly Error IdentityUsernameNotUnique = new Error("Identity.UsernameNotUnique", "Это имя пользователя уже занято.");
}
@@ -0,0 +1,19 @@
using Knot.Modules.Auth.Domain;
namespace Knot.Modules.Auth.Domain;
/// <summary>
/// Интерфейс репозитория для работы с пользователями.
/// </summary>
public interface IUserRepository
{
Task<User?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<List<User>> GetByIdsAsync(IEnumerable<Guid> ids, CancellationToken cancellationToken = default);
Task<User?> GetByUsernameAsync(string username, CancellationToken cancellationToken = default);
Task<bool> IsUsernameUniqueAsync(string username, CancellationToken cancellationToken = default);
Task<List<User>> SearchUsersAsync(string query, CancellationToken cancellationToken = default);
void Add(User user);
void Update(User user);
void Remove(User user);
}
@@ -61,8 +61,8 @@ public static class MongoDbMapConfigurator
BsonClassMap.RegisterClassMap<PollMessage>(cm =>
{
cm.AutoMap();
cm.MapField("_options").SetElementName("Options");
cm.MapField("_votes").SetElementName("Votes");
cm.MapProperty(c => c.Options).SetElementName("_options");
cm.MapProperty(c => c.Votes).SetElementName("_votes");
cm.SetDiscriminator("PollMessage");
});
@@ -0,0 +1,11 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Knot.Modules.Profiles.Domain;
public interface IAvatarStorageService
{
Task<string> UploadAsync(Stream content, string fileName, string contentType, CancellationToken ct = default);
Task DeleteAsync(string fileId, CancellationToken ct = default);
}
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Knot.Modules.Profiles.Domain;
public interface IProfileRepository
{
Task<ProfileDocument?> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<ProfileDocument?> GetByUsernameAsync(string username, CancellationToken ct = default);
Task AddAsync(ProfileDocument profile, CancellationToken ct = default);
Task UpdateAsync(ProfileDocument profile, CancellationToken ct = default);
Task<List<ProfileDocument>> SearchAsync(string query, CancellationToken ct = default);
}
@@ -0,0 +1,9 @@
using System.Threading;
using System.Threading.Tasks;
namespace Knot.Modules.Profiles.Domain;
public interface IProfilesUnitOfWork
{
Task SaveChangesAsync(CancellationToken ct = default);
}
@@ -0,0 +1,10 @@
namespace Knot.Modules.Profiles.Domain;
using Knot.Shared.Kernel;
public static class ProfilesErrors
{
public static readonly Error ProfileNotFound = new("Profile.NotFound", "Profile not found");
}
public static class IdentityErrors
{
public static readonly Error UserNotFound = new("Profile.NotFound", "Profile not found");
}
@@ -0,0 +1,52 @@
using System.Threading;
using System.Threading.Tasks;
using Knot.Modules.Settings.Application.Settings.DTOs;
namespace Knot.Modules.Settings.Application.Settings.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,159 @@
using System.Collections.Generic;
using Knot.Modules.Settings.Application.Settings.DTOs;
namespace Knot.Modules.Settings.Application.Settings.DTOs;
public record PublicConfigDto
{
public SystemConfigDto System { get; init; } = new();
public StoriesConfigDto Stories { get; init; } = new();
public ChatsConfigDto Chats { get; init; } = new();
public MessagesConfigDto Messages { get; init; } = new();
public WebRtcConfigDto WebRtc { get; init; } = new();
public KlipyConfigDto Klipy { get; init; } = new();
public ImportConfigDto Import { get; init; } = new();
public FederationConfigDto Federation { get; init; } = new();
public static PublicConfigDto FromSettings(SystemSettingsDto settings)
{
return new PublicConfigDto
{
System = new SystemConfigDto
{
DomainUrl = settings.System.DomainUrl,
EnableRegistration = settings.System.EnableRegistration
},
Stories = new StoriesConfigDto
{
Enabled = settings.Stories.Enabled,
MaxStoriesPerPeriod = settings.Stories.MaxStoriesPerPeriod,
StoryLifetimeHours = settings.Stories.StoryLifetimeHours,
TextStoriesEnabled = settings.Stories.TextStoriesEnabled,
TextStoryDurationSeconds = settings.Stories.TextStoryDurationSeconds,
MediaStoryMaxDurationSeconds = settings.Stories.MediaStoryMaxDurationSeconds,
MaxMediaSizeBytes = settings.Stories.MaxMediaSizeBytes
},
Chats = new ChatsConfigDto
{
SupportGroups = settings.Chats.SupportGroups,
MaxGroupParticipants = settings.Chats.MaxGroupParticipants,
AllowChatToGroupConversion = settings.Chats.AllowChatToGroupConversion,
EnableFolders = settings.Chats.EnableFolders
},
Messages = new MessagesConfigDto
{
DailyMessageLimitPerUser = settings.Messages.DailyMessageLimitPerUser,
ChatMessageLimit = settings.Messages.ChatMessageLimit,
AllowMedia = settings.Messages.AllowMedia,
MaxMediaSizeBytes = settings.Messages.MaxMediaSizeBytes,
AllowedMediaTypes = settings.Messages.AllowedMediaTypes,
AllowVoiceMessages = settings.Messages.AllowVoiceMessages,
AllowForwarding = settings.Messages.AllowForwarding,
AllowReactions = settings.Messages.AllowReactions,
AllowReplies = settings.Messages.AllowReplies,
AllowQuoting = settings.Messages.AllowQuoting,
AllowMessageDeletion = settings.Messages.AllowMessageDeletion,
ForbidCopying = settings.Messages.ForbidCopying,
AllowLinks = settings.Messages.AllowLinks,
AllowPolls = settings.Messages.AllowPolls,
AllowPinning = settings.Messages.AllowPinning
},
WebRtc = new WebRtcConfigDto
{
Enabled = settings.WebRtc.Enabled,
EnableVoiceCalls = settings.WebRtc.EnableVoiceCalls,
EnableVideoCalls = settings.WebRtc.EnableVideoCalls,
EnableScreenSharing = settings.WebRtc.EnableScreenSharing,
TurnHost = settings.WebRtc.TurnHost,
TurnPort = settings.WebRtc.TurnPort
},
Klipy = new KlipyConfigDto
{
Enabled = settings.Klipy.Enabled,
AppName = settings.Klipy.AppName
},
Import = new ImportConfigDto
{
Enabled = settings.Import.EnableTelegramImport
},
Federation = new FederationConfigDto
{
Enabled = settings.Federation.Enabled,
ServerDescription = settings.Federation.ServerDescription,
AllowedDomains = settings.Federation.AllowedDomains
}
};
}
}
public record SystemConfigDto
{
public string DomainUrl { get; init; } = string.Empty;
public bool EnableRegistration { get; init; }
}
public record StoriesConfigDto
{
public bool Enabled { get; init; }
public int MaxStoriesPerPeriod { get; init; }
public int StoryLifetimeHours { get; init; }
public bool TextStoriesEnabled { get; init; }
public int TextStoryDurationSeconds { get; init; }
public int MediaStoryMaxDurationSeconds { get; init; }
public int MaxMediaSizeBytes { get; init; }
}
public record ChatsConfigDto
{
public bool SupportGroups { get; init; }
public int MaxGroupParticipants { get; init; }
public bool AllowChatToGroupConversion { get; init; }
public bool EnableFolders { get; init; }
}
public record MessagesConfigDto
{
public int DailyMessageLimitPerUser { get; init; }
public int ChatMessageLimit { get; init; }
public bool AllowMedia { get; init; }
public int MaxMediaSizeBytes { get; init; }
public List<string> AllowedMediaTypes { get; init; } = new();
public bool AllowVoiceMessages { get; init; }
public bool AllowForwarding { get; init; }
public bool AllowReactions { get; init; }
public bool AllowReplies { get; init; }
public bool AllowQuoting { get; init; }
public bool AllowMessageDeletion { get; init; }
public bool ForbidCopying { get; init; }
public bool AllowLinks { get; init; }
public bool AllowPolls { get; init; }
public bool AllowPinning { get; init; }
}
public record WebRtcConfigDto
{
public bool Enabled { get; init; }
public bool EnableVoiceCalls { get; init; }
public bool EnableVideoCalls { get; init; }
public bool EnableScreenSharing { get; init; }
public string TurnHost { get; init; } = string.Empty;
public int TurnPort { get; init; }
}
public record KlipyConfigDto
{
public bool Enabled { get; init; }
public string AppName { get; init; } = string.Empty;
}
public record ImportConfigDto
{
public bool Enabled { get; init; }
}
public record FederationConfigDto
{
public bool Enabled { get; init; }
public string ServerDescription { get; init; } = string.Empty;
public List<FederationDomainConfig> AllowedDomains { get; init; } = new();
}
@@ -0,0 +1,112 @@
using System.Collections.Generic;
namespace Knot.Modules.Settings.Application.Settings.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 AutoCleanChats { 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 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 EnableVoiceCalls { get; set; } = true;
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,11 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Knot.Modules.Stories.Application.Abstractions;
public interface IKlipyClient
{
Task<bool> TestConnectionAsync(string apiKey, CancellationToken ct = default);
Task<List<string>> SearchVideosAsync(string apiKey, string query, int limit, CancellationToken ct = default);
}
@@ -0,0 +1,21 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Knot.Modules.Stories.Domain;
using Knot.Modules.Relations.Domain;
namespace Knot.Modules.Stories.Application.Abstractions;
public interface IStoriesDbContext
{
DbSet<Story> Stories { get; }
DbSet<Friendship> Friendships { get; }
DbSet<StoryViewer> StoryViewers { get; }
DbSet<TEntity> Set<TEntity>() where TEntity : class;
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}
public interface IStoriesUnitOfWork
{
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using Knot.Modules.Stories.Application.Abstractions;
using System.Threading;
using System.Threading.Tasks;
namespace Knot.Modules.Stories.Infrastructure.External;
public sealed class KlipyClient : IKlipyClient
{
private readonly HttpClient _httpClient;
public KlipyClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<bool> TestConnectionAsync(string apiKey, CancellationToken ct = default)
{
try
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.klipy.co/v1/trending?limit=1");
request.Headers.Add("X-API-KEY", apiKey);
using var response = await _httpClient.SendAsync(request, ct);
return response.IsSuccessStatusCode;
}
catch
{
return false;
}
}
public async Task<List<string>> SearchVideosAsync(string apiKey, string query, int limit, CancellationToken ct = default)
{
// В реальном проекте: десериализация ответа от Klipy API
return new List<string>();
}
}