diff --git a/.gitignore b/.gitignore
index 4d9a82f..00cc8ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -491,3 +491,5 @@ FodyWeavers.xsd
# JetBrains Rider
*.sln.iml
+
+PaydayBackend/ApiDocumentation.xml
diff --git a/.idea/config/applicationhost.config b/.idea/config/applicationhost.config
new file mode 100644
index 0000000..4b06913
--- /dev/null
+++ b/.idea/config/applicationhost.config
@@ -0,0 +1,997 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PaydayBackend/Controllers/AdminController.cs b/PaydayBackend/Controllers/AdminController.cs
new file mode 100644
index 0000000..c3c03c8
--- /dev/null
+++ b/PaydayBackend/Controllers/AdminController.cs
@@ -0,0 +1,90 @@
+using Microsoft.AspNetCore.Mvc;
+using PaydayBackend.Models;
+using PaydayBackend.Services;
+
+namespace PaydayBackend.Controllers;
+
+[Route("v1/admin")]
+[ApiController]
+public class AdminController : ControllerBase
+{
+ private readonly IAdminService _adminService;
+
+ public AdminController(IAdminService adminService)
+ {
+ _adminService = adminService;
+ }
+
+ // -------------------------------------| Банки |-------------------------------------
+
+ ///
+ /// Добавление банка
+ ///
+ [HttpPost("banks")]
+ public async Task AddBank([FromBody] Bank bank)
+ {
+ await _adminService.AddBank(bank);
+ return Ok();
+ }
+
+ ///
+ /// Получение всех условий одного банка по id
+ ///
+ /// Банк не найден
+ [HttpGet("banks")]
+ public async Task GetAllLoanTermsByBankId(long bankId)
+ {
+ var result = await _adminService.GetAllLoanTermsByBankId(bankId);
+ if (result == null)
+ return BadRequest();
+
+ return Ok(result);
+ }
+
+ // TODO: Переделать
+ ///
+ /// Добавление условия кредитования
+ ///
+ // [HttpPost("banks/{bank_id}/loanterms")]
+ // public async Task AddLoanTerm(long bank_id, [FromBody] LoanTerm loanTerm)
+ // {
+ // await _adminService.AddLoanTerm(loanTerm);
+ // return Ok();
+ // }
+
+ ///
+ /// Удаление ВСЕХ условий кредитования
+ ///
+ /// Банк не найден
+ [HttpGet("banks/{bank_id}/loanterms")]
+ public async Task AddLoanTerm(long bankId)
+ {
+ var result = await _adminService.RemoveAllLoanTermsByBankId(bankId);
+ return result == "OK" ? Ok() : BadRequest(result);
+ }
+
+ // -------------------------------------| Университеты |-------------------------------------
+
+ ///
+ /// Добавление университета
+ ///
+ [HttpPost("universities")]
+ public async Task AddUniversity([FromBody] University university)
+ {
+ await _adminService.AddUniversity(university);
+ return Ok();
+ }
+
+ // TODO: Переделать
+ ///
+ /// Добавление направления университета
+ ///
+ [HttpPost("universities/directions")]
+ public async Task AddUniversityDirection([FromBody] UniversityDirection universityDirection)
+ {
+ await _adminService.AddUniversityDirection(universityDirection);
+ return Ok();
+ }
+
+
+}
\ No newline at end of file
diff --git a/PaydayBackend/Controllers/PublicController.cs b/PaydayBackend/Controllers/PublicController.cs
new file mode 100644
index 0000000..2fb14ea
--- /dev/null
+++ b/PaydayBackend/Controllers/PublicController.cs
@@ -0,0 +1,41 @@
+using Microsoft.AspNetCore.Mvc;
+using PaydayBackend.Models;
+using PaydayBackend.Services;
+
+namespace PaydayBackend.Controllers;
+
+[Route("v1/public")]
+[ApiController]
+public class PublicController : ControllerBase
+{
+ private readonly IPublicService _publicService;
+
+ public PublicController(IPublicService publicService)
+ {
+ _publicService = publicService;
+ }
+
+ ///
+ /// Получение всех университетов
+ ///
+ [HttpGet("university")]
+ public async Task> GetAllUniversity()
+ {
+ return await _publicService.GetAllUniversity();
+ }
+
+ ///
+ /// Получение всех направлений университета
+ ///
+ /// Университет не найден
+ [HttpGet("university/{id}/direction")]
+ public async Task>> GetAllUniversityDirectionByUniversityId(long id)
+ {
+ var result = await _publicService.GetAllUniversityDirectionByUniversityId(id);
+
+ if (result == null)
+ return BadRequest();
+
+ return Ok(result);
+ }
+}
\ No newline at end of file
diff --git a/PaydayBackend/DatabaseContext.cs b/PaydayBackend/DatabaseContext.cs
new file mode 100644
index 0000000..8099948
--- /dev/null
+++ b/PaydayBackend/DatabaseContext.cs
@@ -0,0 +1,19 @@
+using Microsoft.EntityFrameworkCore;
+using PaydayBackend.Models;
+
+namespace PaydayBackend;
+
+public class DatabaseContext : DbContext
+{
+ public DbSet Banks { get; set; } = null!;
+ public DbSet LoanTerms { get; set; } = null!;
+ public DbSet Universities { get; set; } = null!;
+ public DbSet UniversityDirections { get; set; } = null!;
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+
+ }
+
+ public DatabaseContext(DbContextOptions options) : base(options) { }
+}
\ No newline at end of file
diff --git a/PaydayBackend/Models/Bank.cs b/PaydayBackend/Models/Bank.cs
new file mode 100644
index 0000000..7465bc4
--- /dev/null
+++ b/PaydayBackend/Models/Bank.cs
@@ -0,0 +1,11 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace PaydayBackend.Models;
+
+public class Bank
+{
+ [Key]
+ public long Id { get; set; }
+ public string Name { get; set; }
+ public string ImageUrl { get; set; }
+}
\ No newline at end of file
diff --git a/PaydayBackend/Models/LoanTerm.cs b/PaydayBackend/Models/LoanTerm.cs
new file mode 100644
index 0000000..7c27c9d
--- /dev/null
+++ b/PaydayBackend/Models/LoanTerm.cs
@@ -0,0 +1,15 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace PaydayBackend.Models;
+
+public class LoanTerm
+{
+ [Key]
+ public long Id { get; set; }
+ public Bank Bank { get; set; }
+ [Range(0f, 360f)]
+ public float InterestRate { get; set; }
+ public int LoanTermInMonths { get; set; }
+ public string Documents { get; set; }
+
+}
\ No newline at end of file
diff --git a/PaydayBackend/Models/University.cs b/PaydayBackend/Models/University.cs
new file mode 100644
index 0000000..c99ae1b
--- /dev/null
+++ b/PaydayBackend/Models/University.cs
@@ -0,0 +1,12 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace PaydayBackend.Models;
+
+public class University
+{
+ [Key]
+ public long Id { get; set; }
+ public string Name { get; set; }
+ public string FullName { get; set; }
+ public string ImageUrl { get; set; }
+}
\ No newline at end of file
diff --git a/PaydayBackend/Models/UniversityDirection.cs b/PaydayBackend/Models/UniversityDirection.cs
new file mode 100644
index 0000000..7137a26
--- /dev/null
+++ b/PaydayBackend/Models/UniversityDirection.cs
@@ -0,0 +1,12 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace PaydayBackend.Models;
+
+public class UniversityDirection
+{
+ [Key]
+ public long UniversityId { get; set; }
+ public string Code { get; set; }
+ public string Name { get; set; }
+ public int BudgetPlaces { get; set; }
+}
\ No newline at end of file
diff --git a/PaydayBackend/PaydayBackend.csproj b/PaydayBackend/PaydayBackend.csproj
index 615a08c..1c6a0d5 100644
--- a/PaydayBackend/PaydayBackend.csproj
+++ b/PaydayBackend/PaydayBackend.csproj
@@ -7,8 +7,24 @@
Linux
+
+ ApiDocumentation.xml
+
+
+
+ ApiDocumentation.xml
+ 1701;1702;IL2121,1591
+
+
+
+
+
+
+
+
+
@@ -18,8 +34,4 @@
-
-
-
-
diff --git a/PaydayBackend/Program.cs b/PaydayBackend/Program.cs
index 8264bac..7f1e4ad 100644
--- a/PaydayBackend/Program.cs
+++ b/PaydayBackend/Program.cs
@@ -1,24 +1,75 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Microsoft.OpenApi.Models;
+using Minio;
+using Minio.AspNetCore;
+using Minio.AspNetCore.HealthChecks;
+using PaydayBackend;
+using PaydayBackend.Services;
+
var builder = WebApplication.CreateBuilder(args);
-// Add services to the container.
+string GetEnv(string envName, string settingsName = "")
+{
+ string? dbConString = builder.Configuration.GetConnectionString(settingsName) ?? Environment.GetEnvironmentVariable(envName);
+ if (!string.IsNullOrEmpty(dbConString))
+ return dbConString;
+
+ Console.WriteLine($"Environment variable {envName} not found.");
+ return String.Empty;
+}
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
-builder.Services.AddSwaggerGen();
+builder.Services.AddSwaggerGen(c =>
+{
+ c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "ApiDocumentation.xml"));
+ c.SwaggerDoc("v1", new OpenApiInfo { Title = "Backend", Version = "v1" });
+});
+
+// Database
+string? dbConString = GetEnv("CONNECTION_STRING", "DefaultConnection");
+
+builder.Services.AddDbContext(options =>
+ { options.UseNpgsql(dbConString); });
+
+// HealthChecks
+builder.Services.AddHealthChecks()
+ .AddNpgSql(dbConString)
+ .AddMinio(failureStatus: HealthStatus.Degraded, factory: sp => sp.GetRequiredService());
+
+// Services
+builder.Services.AddSingleton();
+builder.Services.AddScoped();
+builder.Services.AddScoped();
+
+// Minio
+builder.Services.AddMinio(options =>
+{
+ options.Endpoint = GetEnv("S3_ENDPOINT", "S3Endpoint");
+ options.AccessKey = GetEnv("S3_ACCESS_KEY", "S3AccessKey");
+ options.SecretKey = GetEnv("S3_SECRET_KEY", "S3SecretKey");
+
+ options.ConfigureClient(client =>
+ {
+ client.WithSSL(false);
+ });
+});
var app = builder.Build();
// Configure the HTTP request pipeline.
-if (app.Environment.IsDevelopment())
-{
+// if (app.Environment.IsDevelopment())
+// {
app.UseSwagger();
app.UseSwaggerUI();
-}
+// }
-app.UseHttpsRedirection();
+app.MapHealthChecks("/health");
+// app.UseHttpsRedirection();
-app.UseAuthorization();
+// app.UseAuthorization();
app.MapControllers();
diff --git a/PaydayBackend/Services/AdminService.cs b/PaydayBackend/Services/AdminService.cs
new file mode 100644
index 0000000..37c4909
--- /dev/null
+++ b/PaydayBackend/Services/AdminService.cs
@@ -0,0 +1,85 @@
+using Microsoft.EntityFrameworkCore;
+using PaydayBackend.Models;
+
+namespace PaydayBackend.Services;
+
+public interface IAdminService
+{
+ public Task AddBank(Bank bank);
+ public Task> GetAllBanks();
+ public Task AddLoanTerm(LoanTerm loanTerm);
+ public Task RemoveAllLoanTermsByBankId(long bankId);
+ public Task?> GetAllLoanTermsByBankId(long bankId);
+ public Task AddUniversity(University university);
+ public Task AddUniversityDirection(UniversityDirection universityDirection);
+}
+
+public class AdminService : IAdminService
+{
+ private DatabaseContext _databaseContext;
+
+ private async Task BankIsExist(long bankId)
+ {
+ if (await _databaseContext.Banks.Where(x => x.Id == bankId).FirstOrDefaultAsync() == null)
+ return false;
+
+ return true;
+ }
+
+ public AdminService(DatabaseContext databaseContext)
+ {
+ _databaseContext = databaseContext;
+ }
+
+ // -------------------------------------| Банки |-------------------------------------
+
+ public async Task AddBank(Bank bank)
+ {
+ await _databaseContext.Banks.AddAsync(bank);
+ await _databaseContext.SaveChangesAsync();
+ }
+
+ public async Task> GetAllBanks()
+ {
+ return await _databaseContext.Banks.ToListAsync();
+ }
+
+ public async Task AddLoanTerm(LoanTerm loanTerm)
+ {
+ await _databaseContext.LoanTerms.AddAsync(loanTerm);
+ await _databaseContext.SaveChangesAsync();
+ }
+
+ public async Task RemoveAllLoanTermsByBankId(long bankId)
+ {
+ if (await BankIsExist(bankId))
+ return "Bank not found";
+
+ var result = await _databaseContext.LoanTerms.Where(x => x.Bank.Id == bankId).ToListAsync();
+ _databaseContext.LoanTerms.RemoveRange(result);
+
+ return "OK";
+ }
+
+ public async Task?> GetAllLoanTermsByBankId(long bankId)
+ {
+ if (await BankIsExist(bankId))
+ return null;
+
+ return await _databaseContext.LoanTerms.Where(x => x.Bank.Id == bankId).ToListAsync();
+ }
+
+ // -------------------------------------| Университеты |-------------------------------------
+
+ public async Task AddUniversity(University university)
+ {
+ await _databaseContext.Universities.AddAsync(university);
+ await _databaseContext.SaveChangesAsync();
+ }
+
+ public async Task AddUniversityDirection(UniversityDirection universityDirection)
+ {
+ await _databaseContext.UniversityDirections.AddAsync(universityDirection);
+ await _databaseContext.SaveChangesAsync();
+ }
+}
\ No newline at end of file
diff --git a/PaydayBackend/Services/IStorageService.cs b/PaydayBackend/Services/IStorageService.cs
new file mode 100644
index 0000000..241f285
--- /dev/null
+++ b/PaydayBackend/Services/IStorageService.cs
@@ -0,0 +1,124 @@
+using Minio;
+using Minio.AspNetCore;
+using Minio.Exceptions;
+using PaydayBackend.Utils;
+
+namespace PaydayBackend.Services;
+
+public interface IStorageService
+{
+ public Task UploadFile(MemoryStream fileStream, string bucket, string fileName);
+ public Task GetFile(string bucket, string fileName);
+ public Task RemoveFile(string bucket, string fileName);
+ public Task IsFileExist(string bucket, string fileName);
+}
+
+public class StorageService : IStorageService
+{
+ private MinioClient _minio;
+
+ public StorageService(IMinioClientFactory minioClientFactory)
+ {
+ _minio = minioClientFactory.CreateClient();
+ }
+
+ public async Task GetFile(string bucket, string fileName)
+ {
+ if (!await IsFileExist(bucket, fileName))
+ return null;
+
+ try
+ {
+ var outStream = new MemoryStream();
+ GetObjectArgs getObjectArgs = new GetObjectArgs()
+ .WithBucket(bucket)
+ .WithObject(fileName)
+ .WithCallbackStream((stream) =>
+ {
+ stream.CopyTo(outStream);
+ outStream.Position = 0;
+ stream.Close();
+ });
+ await _minio.GetObjectAsync(getObjectArgs);
+
+ return outStream;
+ }
+ catch (MinioException e)
+ {
+ Console.WriteLine("File upload error: {0}", e.Message);
+ return null;
+ }
+ }
+
+ public async Task RemoveFile(string bucket, string fileName)
+ {
+ try
+ {
+ RemoveObjectArgs rmArgs = new RemoveObjectArgs()
+ .WithBucket(bucket)
+ .WithObject(fileName);
+ await _minio.RemoveObjectAsync(rmArgs);
+ Console.WriteLine($"File \"{fileName}\" removed successfully");
+ return true;
+ }
+ catch (MinioException e)
+ {
+ Console.WriteLine($"File \"{fileName}\" remove error: " + e);
+ return false;
+ }
+ }
+
+ public async Task IsFileExist(string bucket, string fileName)
+ {
+ try
+ {
+ // Проверка, существует ли объект
+ // Если объект не найден, statObjectArgs генерирует исключение
+ StatObjectArgs statObjectArgs = new StatObjectArgs()
+ .WithBucket(bucket)
+ .WithObject(fileName);
+ await _minio.StatObjectAsync(statObjectArgs);
+ return true;
+ }
+ catch (MinioException e)
+ {
+ Console.WriteLine($"File \"{fileName}\" not exist: " + e);
+ return false;
+ }
+ }
+
+ public async Task UploadFile(MemoryStream fileStream, string bucket, string fileName)
+ {
+ fileStream.Position = 0;
+
+ try
+ {
+ // Создаём ведро если его нет
+ var beArgs = new BucketExistsArgs().WithBucket(bucket);
+ bool found = await _minio.BucketExistsAsync(beArgs).ConfigureAwait(false);
+ if (!found)
+ {
+ var mbArgs = new MakeBucketArgs().WithBucket(bucket);
+ await _minio.MakeBucketAsync(mbArgs).ConfigureAwait(false);
+ }
+
+ // Загружаем файл
+ var putObjectArgs = new PutObjectArgs()
+ .WithBucket(bucket)
+ .WithObject(fileName)
+ .WithStreamData(fileStream)
+ .WithObjectSize(fileStream.Length)
+ .WithContentType(FileHelper.GetMimeType(fileName));
+ await _minio.PutObjectAsync(putObjectArgs).ConfigureAwait(false);
+ Console.WriteLine("File uploaded successfully: " + fileName);
+ fileStream.Close();
+ return true;
+ }
+ catch (MinioException e)
+ {
+ Console.WriteLine("File upload error: {0}", e.Message);
+ fileStream.Close();
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PaydayBackend/Services/PublicService.cs b/PaydayBackend/Services/PublicService.cs
new file mode 100644
index 0000000..cfc8e99
--- /dev/null
+++ b/PaydayBackend/Services/PublicService.cs
@@ -0,0 +1,35 @@
+using Microsoft.EntityFrameworkCore;
+using PaydayBackend.Models;
+
+namespace PaydayBackend.Services;
+
+public interface IPublicService
+{
+ public Task> GetAllUniversity();
+ public Task?> GetAllUniversityDirectionByUniversityId(long universityId);
+ // public Task GetAllLoansByDirectionCost();
+}
+
+public class PublicService : IPublicService
+{
+ private DatabaseContext _databaseContext;
+
+ public PublicService(DatabaseContext databaseContext)
+ {
+ _databaseContext = databaseContext;
+ }
+
+ public async Task> GetAllUniversity()
+ {
+ return await _databaseContext.Universities.ToListAsync();
+ }
+
+ public async Task?> GetAllUniversityDirectionByUniversityId(long universityId)
+ {
+ if (await _databaseContext.Universities.Where(x => x.Id == universityId).FirstOrDefaultAsync() == null)
+ return null;
+
+ return await _databaseContext.UniversityDirections.Where(x => x.UniversityId == universityId).ToListAsync();
+ }
+
+}
\ No newline at end of file
diff --git a/PaydayBackend/Utils/FileHelper.cs b/PaydayBackend/Utils/FileHelper.cs
new file mode 100644
index 0000000..c2fdb4f
--- /dev/null
+++ b/PaydayBackend/Utils/FileHelper.cs
@@ -0,0 +1,90 @@
+using SixLabors.ImageSharp.Formats.Jpeg;
+
+namespace PaydayBackend.Utils;
+
+public static class FileHelper
+{
+ public static string GetMimeType(string filename)
+ {
+ var extension = filename.Split(".").Last();
+ switch (extension)
+ {
+ case "jpg":
+ case "jpeg":
+ return "image/jpeg";
+ case "png":
+ return "image/png";
+ case "csv":
+ return "text/csv";
+ case "pdf":
+ return "application/pdf";
+ case "html":
+ return "text/html";
+ default:
+ throw new ArgumentException($"Unsupported file type, file: {filename}");
+ }
+ }
+
+ // Функция валидации размера файла
+ public static string ValidateMaxFileSize(IFormFile file, int maxSizeMb, string[] allowedExtensions)
+ {
+ if (file.Length > 0)
+ {
+ if (file.Length > maxSizeMb * 1024 * 1024)
+ return $"File need to be less than {maxSizeMb}MB.";
+ if (!allowedExtensions.Contains(Path.GetExtension(file.FileName).ToLower()))
+ return "Invalid file extension.";
+
+ return "OK";
+ }
+
+ return "Invalid file.";
+ }
+
+ // Функция валидации изображения
+ public static async Task ValidateImage(MemoryStream fileStream, string[]? allowedExtensions, int minWidth = 0,
+ int minHeight = 0, int maxWidth = 0, int maxHeight = 0)
+ {
+ if (allowedExtensions == null)
+ allowedExtensions = new[] {"JPG", "JPEG"};
+
+ try
+ {
+ var imageInfo = await Image.DetectFormatAsync(fileStream);
+ if (!allowedExtensions.Contains(imageInfo.Name))
+ return "Image type is not" + allowedExtensions.Aggregate("",
+ (current, next) => current + " " + next) + ".";
+
+ using var image = await Image.LoadAsync(fileStream);
+ if (image.Width < minWidth || image.Height < minHeight)
+ return $"Image size must be at least {minWidth}x{minHeight}.";
+
+ if (maxHeight != 0 && maxWidth != 0)
+ if (image.Width > maxWidth || image.Height > maxHeight)
+ return $"Image size must be less than {maxWidth}x{maxHeight}.";
+
+ return "OK";
+ }
+ catch (ImageFormatException e)
+ {
+ Console.WriteLine(e.Message);
+ return "Invalid image type.";
+ }
+ }
+
+ // Кропает изображение по центру, в зависимости от того, какая сторона меньше
+ public static async Task CropImage(MemoryStream fileStream)
+ {
+ fileStream.Position = 0;
+ using var image = await Image.LoadAsync(fileStream);
+ Console.WriteLine(image.Height/2 - image.Width/2 + " " + image.Width + " " + image.Height);
+ if (image.Height > image.Width)
+ image.Mutate(i => i.Crop(new Rectangle(0, image.Height/2 - image.Width/2, image.Width, image.Width)));
+ else
+ image.Mutate(i => i.Crop(new Rectangle(image.Width/2 - image.Height/2, 0, image.Height, image.Height)));
+
+ await fileStream.FlushAsync();
+ fileStream.Position = 0;
+ await image.SaveAsync(fileStream, new JpegEncoder());
+ }
+}
\ No newline at end of file