Форматирование

This commit is contained in:
Халимов Рустам
2026-03-19 01:31:43 +03:00
parent 282b43d4c1
commit 8f2de0e9bf
90 changed files with 901 additions and 299 deletions
@@ -21,9 +21,9 @@ public static class DependencyInjection
services.AddScoped<IUserContext, UserContext>();
// Настройка шифрования
var masterKey = configuration["KNOT_MASTER_ENCRYPTION_KEY"]
var masterKey = configuration["KNOT_MASTER_ENCRYPTION_KEY"]
?? throw new ArgumentNullException("KNOT_MASTER_ENCRYPTION_KEY is missing in env.");
services.AddSingleton<IEncryptionService>(new AesEncryptionService(masterKey));
// Настройка MinIO (S3)
@@ -38,7 +38,7 @@ public static class DependencyInjection
.WithSSL(false)
.Build());
services.AddScoped<IFileStorageService>(provider =>
services.AddScoped<IFileStorageService>(provider =>
{
var minioClient = provider.GetRequiredService<IMinioClient>();
var encService = provider.GetRequiredService<IEncryptionService>();
@@ -46,10 +46,10 @@ public static class DependencyInjection
});
// Настройка System Database
var connectionString = configuration.GetConnectionString("DefaultConnection")
var connectionString = configuration.GetConnectionString("DefaultConnection")
?? configuration["DATABASE_URL"]
?? throw new ArgumentNullException("Database connection string not found.");
services.AddDbContext<SystemDbContext>(options =>
options.UseNpgsql(connectionString));
@@ -40,7 +40,7 @@ public class AdminAuthMiddleware
var authHeader = AuthenticationHeaderValue.Parse(context.Request.Headers["Authorization"]);
var credentialBytes = Convert.FromBase64String(authHeader.Parameter ?? string.Empty);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':', 2);
var username = credentials[0];
var password = credentials[1];
@@ -32,7 +32,7 @@ public sealed class ExceptionHandlingMiddleware
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
var (statusCode, message) = exception switch
{
UnauthorizedAccessException => (HttpStatusCode.Unauthorized, "Unauthorized access."),
@@ -22,8 +22,8 @@ public class SystemDbContextFactory : IDesignTimeDbContextFactory<SystemDbContex
.Build();
var builder = new DbContextOptionsBuilder<SystemDbContext>();
var connectionString = configuration.GetConnectionString("DefaultConnection")
?? configuration["DATABASE_URL"]
var connectionString = configuration.GetConnectionString("DefaultConnection")
?? configuration["DATABASE_URL"]
?? "Host=localhost;Port=5432;Database=knot_db;Username=knot;Password=knot_pass";
builder.UseNpgsql(connectionString);
@@ -29,17 +29,22 @@ public class AesEncryptionService : IEncryptionService
}
if (_key.Length != 32)
{
throw new ArgumentException("Мастер-ключ должен быть ровно 32 байта для AES-256.");
}
}
// Сообщения: AES-256-GCM
public string EncryptMessage(string plainText)
{
if (string.IsNullOrEmpty(plainText)) return plainText;
if (string.IsNullOrEmpty(plainText))
{
return plainText;
}
var nonce = new byte[12];
RandomNumberGenerator.Fill(nonce);
var plainBytes = Encoding.UTF8.GetBytes(plainText);
var cipherBytes = new byte[plainBytes.Length];
var tag = new byte[16];
@@ -53,10 +58,16 @@ public class AesEncryptionService : IEncryptionService
public string DecryptMessage(string cipherText)
{
if (string.IsNullOrEmpty(cipherText)) return cipherText;
if (string.IsNullOrEmpty(cipherText))
{
return cipherText;
}
var parts = cipherText.Split(':');
if (parts.Length != 3) return cipherText;
if (parts.Length != 3)
{
return cipherText;
}
try
{
@@ -49,28 +49,31 @@ public class StatisticsService : IStatisticsService
long totalDiskSpace = 5L * 1024 * 1024 * 1024 * 1024; // Fallback
long freeSpace = 0;
try
try
{
var drive = new System.IO.DriveInfo("/");
if(drive.IsReady)
if (drive.IsReady)
{
totalDiskSpace = drive.TotalSize;
freeSpace = drive.AvailableFreeSpace;
}
else
}
else
{
drive = new System.IO.DriveInfo(System.IO.Directory.GetCurrentDirectory());
totalDiskSpace = drive.TotalSize;
freeSpace = drive.AvailableFreeSpace;
}
}
}
catch { }
var usedSpace = latestStat?.TotalFilesSize ?? 0;
long onlineUsersCount = _cache.TryGetValue("Global_OnlineUsersCount", out int count) ? count : 0;
long offlineUsersCount = totalUsers - onlineUsersCount;
if(offlineUsersCount < 0) offlineUsersCount = 0;
if (offlineUsersCount < 0)
{
offlineUsersCount = 0;
}
var stats = new DashboardStatsDto
{
@@ -78,7 +81,7 @@ public class StatisticsService : IStatisticsService
StorageLimitBytes = freeSpace > 0 ? usedSpace + freeSpace : totalDiskSpace,
OnlineUsers = onlineUsersCount,
OfflineUsers = offlineUsersCount,
TotalUsers = totalUsers,
TotalUsers = totalUsers,
ActivityTimeline = history
};
@@ -97,7 +100,10 @@ internal static class SqlExtensions
{
cmd.CommandText = "SELECT COUNT(*) FROM identity.\"Users\"";
if (cmd.Connection?.State != System.Data.ConnectionState.Open)
{
await ((System.Data.Common.DbConnection)cmd.Connection!).OpenAsync();
}
var result = await ((System.Data.Common.DbCommand)cmd).ExecuteScalarAsync();
return Convert.ToInt64(result);
}
@@ -30,7 +30,7 @@ public class StatisticsWorker : BackgroundService
// Simplified gathering for daily stats
var today = DateTime.UtcNow.Date;
var stat = await db.DailyStats.FirstOrDefaultAsync(s => s.Date == today, stoppingToken);
if (stat == null)
{
stat = new DailyStat { Date = today };
@@ -41,10 +41,10 @@ public class StatisticsWorker : BackgroundService
var cmd = conn.CreateCommand();
stat.ActiveUsers = await cmd.QueryTotalUsersAsync();
long dbSize = 0;
long filesSize = 0;
try
try
{
// 1. Database size itself
cmd.CommandText = "SELECT pg_database_size(current_database());";
@@ -59,7 +59,7 @@ public class StatisticsWorker : BackgroundService
catch { }
stat.TotalFilesSize = dbSize + filesSize; // Database size + MinIO files size
await db.SaveChangesAsync(stoppingToken);
}
catch
@@ -118,9 +118,20 @@ public class S3FileStorageService : IFileStorageService
var statArgs = new StatObjectArgs().WithBucket(_bucketName).WithObject(fileId);
var stat = await _minioClient.StatObjectAsync(statArgs).ConfigureAwait(false);
if (stat.MetaData.ContainsKey("Contenttype")) contentType = stat.MetaData["Contenttype"];
if (stat.MetaData.ContainsKey("Originalfilename")) fileName = stat.MetaData["Originalfilename"];
if (stat.MetaData.ContainsKey("Iv")) ivBase64 = stat.MetaData["Iv"];
if (stat.MetaData.ContainsKey("Contenttype"))
{
contentType = stat.MetaData["Contenttype"];
}
if (stat.MetaData.ContainsKey("Originalfilename"))
{
fileName = stat.MetaData["Originalfilename"];
}
if (stat.MetaData.ContainsKey("Iv"))
{
ivBase64 = stat.MetaData["Iv"];
}
// Загружаем во временный файл (так как CryptoStream требует правильного чтения/записи)
var getObjArgs = new GetObjectArgs()
@@ -171,7 +182,7 @@ public class S3FileStorageService : IFileStorageService
try
{
var listArgs = new ListObjectsArgs().WithBucket(_bucketName).WithRecursive(true);
await foreach (var item in _minioClient.ListObjectsEnumAsync(listArgs).ConfigureAwait(false))
{
result.Add((item.Key, (long)item.Size));
@@ -22,7 +22,7 @@ public class SystemSettingsDto
// Confederation
public bool EnableConfederation { get; set; } = false;
public List<string> AllowedDomains { get; set; } = new();
// Auth
public bool EnableRegistration { get; set; } = true;
}
@@ -4,12 +4,12 @@ public static class Klipy
{
public const string ApiUrlCo = "https://api.klipy.co/api/v1/{0}/{1}?page=1&per_page=30&{2}&customer_id={3}";
public const string ApiUrlCom = "https://api.klipy.com/api/v1/{0}/{1}?page=1&per_page=30&{2}&customer_id={3}";
public const string ResourceTrending = "gifs/trending";
public const string ResourceSearch = "gifs/search";
public const int TrendingCacheMinutes = 60;
public const int SearchCacheMinutes = 15;
public const string DefaultCustomerId = "anonymous";
}
@@ -21,8 +21,16 @@ public abstract class Entity<TId> : IEquatable<Entity<TId>>
public bool Equals(Entity<TId>? other)
{
if (other is null) return false;
if (ReferenceEquals(this, other)) return true;
if (other is null)
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Id.Equals(other.Id);
}
@@ -7,7 +7,9 @@ public interface ICommand : IRequest<Result> { }
public interface ICommand<TResponse> : IRequest<Result<TResponse>> { }
public interface ICommandHandler<in TCommand> : IRequestHandler<TCommand, Result>
where TCommand : ICommand { }
where TCommand : ICommand
{ }
public interface ICommandHandler<in TCommand, TResponse> : IRequestHandler<TCommand, Result<TResponse>>
where TCommand : ICommand<TResponse> { }
where TCommand : ICommand<TResponse>
{ }
@@ -20,9 +20,14 @@ public class Result
protected Result(bool isSuccess, Error error)
{
if (isSuccess && error != Error.None)
{
throw new InvalidOperationException();
}
if (!isSuccess && error == Error.None)
{
throw new InvalidOperationException();
}
IsSuccess = isSuccess;
Error = error;
@@ -10,7 +10,7 @@ public interface IEncryptionService
// Создает поток шифрования для потоковой передачи файлов "на лету"
Stream CreateEncryptionStream(Stream unencryptedOutputStream, out byte[] iv);
// Создает поток дешифрования для чтения файлов "на лету"
Stream CreateDecryptionStream(Stream encryptedInputStream, byte[] iv);
}
@@ -15,7 +15,11 @@ public abstract class ValueObject : IEquatable<ValueObject>
public bool Equals(ValueObject? other)
{
if (other is null) return false;
if (other is null)
{
return false;
}
return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
}